Plugins/CheckerPlugins/CodeStyleChecker/pep8.py

branch
Py2 comp.
changeset 3057
10516539f238
parent 3056
9986ec0e559a
parent 3004
c4bf32c791d0
child 3145
a9de05d4a22f
equal deleted inserted replaced
3056:9986ec0e559a 3057:10516539f238
1 # -*- coding: utf-8 -*-
2
3 #
4 # pep8.py - Check Python source code formatting, according to PEP 8
5 # Copyright (C) 2006-2009 Johann C. Rocholl <johann@rocholl.net>
6 # Copyright (C) 2009-2013 Florent Xicluna <florent.xicluna@gmail.com>
7 #
8 # Permission is hereby granted, free of charge, to any person
9 # obtaining a copy of this software and associated documentation files
10 # (the "Software"), to deal in the Software without restriction,
11 # including without limitation the rights to use, copy, modify, merge,
12 # publish, distribute, sublicense, and/or sell copies of the Software,
13 # and to permit persons to whom the Software is furnished to do so,
14 # subject to the following conditions:
15 #
16 # The above copyright notice and this permission notice shall be
17 # included in all copies or substantial portions of the Software.
18 #
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 # SOFTWARE.
27
28 """
29 Check Python source code formatting, according to PEP 8:
30 http://www.python.org/dev/peps/pep-0008/
31
32 For usage and a list of options, try this:
33 $ python pep8.py -h
34
35 This program and its regression test suite live here:
36 http://github.com/jcrocholl/pep8
37
38 Groups of errors and warnings:
39 E errors
40 W warnings
41 100 indentation
42 200 whitespace
43 300 blank lines
44 400 imports
45 500 line length
46 600 deprecation
47 700 statements
48 900 syntax error
49 """
50
51 from __future__ import unicode_literals # __IGNORE_WARNING__
52
53 #
54 # This is a modified version to make the original pep8.py better suitable
55 # for being called from within the eric5 IDE. The modifications are as
56 # follows:
57 #
58 # - made messages translatable via Qt
59 # - added code for eric5 integration
60 #
61 # Copyright (c) 2011 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
62 #
63
64 __version__ = '1.4.6'
65
66 import os
67 import sys
68 import re
69 import time
70 import inspect
71 import keyword
72 import tokenize
73 from optparse import OptionParser
74 from fnmatch import fnmatch
75 try:
76 from configparser import RawConfigParser
77 from io import TextIOWrapper
78 except ImportError:
79 from ConfigParser import RawConfigParser # __IGNORE_WARNING__
80
81 from PyQt4.QtCore import QCoreApplication, QT_TRANSLATE_NOOP
82
83 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git,__pycache__'
84 DEFAULT_IGNORE = 'E123,E226,E24'
85 if sys.platform == 'win32':
86 DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8')
87 else:
88 DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or
89 os.path.expanduser('~/.config'), 'pep8')
90 PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8')
91 TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite')
92 MAX_LINE_LENGTH = 79
93 REPORT_FORMAT = {
94 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s',
95 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s',
96 }
97
98 PyCF_ONLY_AST = 1024
99 SINGLETONS = frozenset(['False', 'None', 'True'])
100 KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS
101 UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-'])
102 ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-'])
103 WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%'])
104 WS_NEEDED_OPERATORS = frozenset([
105 '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>',
106 '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '='])
107 WHITESPACE = frozenset(' \t')
108 SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE,
109 tokenize.INDENT, tokenize.DEDENT])
110 BENCHMARK_KEYS = ['directories', 'files', 'logical lines', 'physical lines']
111
112 INDENT_REGEX = re.compile(r'([ \t]*)')
113 RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,')
114 RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,\s*\w+\s*,\s*\w+')
115 ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b')
116 DOCSTRING_REGEX = re.compile(r'u?r?["\']')
117 EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
118 WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)')
119 COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)')
120 COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type'
121 r'|\s*\(\s*([^)]*[^ )])\s*\))')
122 KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS))
123 OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)')
124 LAMBDA_REGEX = re.compile(r'\blambda\b')
125 HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$')
126
127 # Work around Python < 2.6 behaviour, which does not generate NL after
128 # a comment which is on a line by itself.
129 COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n'
130
131
132 ##############################################################################
133 # Helper functions for translated and formatted messages
134 ##############################################################################
135
136
137 pep8_messages = {
138 "E101": QT_TRANSLATE_NOOP("pep8",
139 "indentation contains mixed spaces and tabs"),
140 "E111": QT_TRANSLATE_NOOP("pep8",
141 "indentation is not a multiple of four"),
142 "E112": QT_TRANSLATE_NOOP("pep8",
143 "expected an indented block"),
144 "E113": QT_TRANSLATE_NOOP("pep8",
145 "unexpected indentation"),
146 "E121": QT_TRANSLATE_NOOP("pep8",
147 "continuation line indentation is not a multiple of four"),
148 "E122": QT_TRANSLATE_NOOP("pep8",
149 "continuation line missing indentation or outdented"),
150 "E123": QT_TRANSLATE_NOOP("pep8",
151 "closing bracket does not match indentation of opening"
152 " bracket's line"),
153 "E124": QT_TRANSLATE_NOOP("pep8",
154 "closing bracket does not match visual indentation"),
155 "E125": QT_TRANSLATE_NOOP("pep8",
156 "continuation line does not distinguish itself from next"
157 " logical line"),
158 "E126": QT_TRANSLATE_NOOP("pep8",
159 "continuation line over-indented for hanging indent"),
160 "E127": QT_TRANSLATE_NOOP("pep8",
161 "continuation line over-indented for visual indent"),
162 "E128": QT_TRANSLATE_NOOP("pep8",
163 "continuation line under-indented for visual indent"),
164 "E133": QT_TRANSLATE_NOOP("pep8",
165 "closing bracket is missing indentation"),
166 "W191": QT_TRANSLATE_NOOP("pep8",
167 "indentation contains tabs"),
168 "E201": QT_TRANSLATE_NOOP("pep8",
169 "whitespace after '{0}'"),
170 "E202": QT_TRANSLATE_NOOP("pep8",
171 "whitespace before '{0}'"),
172 "E203": QT_TRANSLATE_NOOP("pep8",
173 "whitespace before '{0}'"),
174 "E211": QT_TRANSLATE_NOOP("pep8",
175 "whitespace before '{0}'"),
176 "E221": QT_TRANSLATE_NOOP("pep8",
177 "multiple spaces before operator"),
178 "E222": QT_TRANSLATE_NOOP("pep8",
179 "multiple spaces after operator"),
180 "E223": QT_TRANSLATE_NOOP("pep8",
181 "tab before operator"),
182 "E224": QT_TRANSLATE_NOOP("pep8",
183 "tab after operator"),
184 "E225": QT_TRANSLATE_NOOP("pep8",
185 "missing whitespace around operator"),
186 "E226": QT_TRANSLATE_NOOP("pep8",
187 "missing whitespace around arithmetic operator"),
188 "E227": QT_TRANSLATE_NOOP("pep8",
189 "missing whitespace around bitwise or shift operator"),
190 "E228": QT_TRANSLATE_NOOP("pep8",
191 "missing whitespace around modulo operator"),
192 "E231": QT_TRANSLATE_NOOP("pep8",
193 "missing whitespace after '{0}'"),
194 "E241": QT_TRANSLATE_NOOP("pep8",
195 "multiple spaces after '{0}'"),
196 "E242": QT_TRANSLATE_NOOP("pep8",
197 "tab after '{0}'"),
198 "E251": QT_TRANSLATE_NOOP("pep8",
199 "unexpected spaces around keyword / parameter equals"),
200 "E261": QT_TRANSLATE_NOOP("pep8",
201 "at least two spaces before inline comment"),
202 "E262": QT_TRANSLATE_NOOP("pep8",
203 "inline comment should start with '# '"),
204 "E271": QT_TRANSLATE_NOOP("pep8",
205 "multiple spaces after keyword"),
206 "E272": QT_TRANSLATE_NOOP("pep8",
207 "multiple spaces before keyword"),
208 "E273": QT_TRANSLATE_NOOP("pep8",
209 "tab after keyword"),
210 "E274": QT_TRANSLATE_NOOP("pep8",
211 "tab before keyword"),
212 "W291": QT_TRANSLATE_NOOP("pep8",
213 "trailing whitespace"),
214 "W292": QT_TRANSLATE_NOOP("pep8",
215 "no newline at end of file"),
216 "W293": QT_TRANSLATE_NOOP("pep8",
217 "blank line contains whitespace"),
218 "E301": QT_TRANSLATE_NOOP("pep8",
219 "expected 1 blank line, found 0"),
220 "E302": QT_TRANSLATE_NOOP("pep8",
221 "expected 2 blank lines, found {0}"),
222 "E303": QT_TRANSLATE_NOOP("pep8",
223 "too many blank lines ({0})"),
224 "E304": QT_TRANSLATE_NOOP("pep8",
225 "blank lines found after function decorator"),
226 "W391": QT_TRANSLATE_NOOP("pep8",
227 "blank line at end of file"),
228 "E401": QT_TRANSLATE_NOOP("pep8",
229 "multiple imports on one line"),
230 "E501": QT_TRANSLATE_NOOP("pep8",
231 "line too long ({0} > {1} characters)"),
232 "E502": QT_TRANSLATE_NOOP("pep8",
233 "the backslash is redundant between brackets"),
234 "W601": QT_TRANSLATE_NOOP("pep8",
235 ".has_key() is deprecated, use 'in'"),
236 "W602": QT_TRANSLATE_NOOP("pep8",
237 "deprecated form of raising exception"),
238 "W603": QT_TRANSLATE_NOOP("pep8",
239 "'<>' is deprecated, use '!='"),
240 "W604": QT_TRANSLATE_NOOP("pep8",
241 "backticks are deprecated, use 'repr()'"),
242 "E701": QT_TRANSLATE_NOOP("pep8",
243 "multiple statements on one line (colon)"),
244 "E702": QT_TRANSLATE_NOOP("pep8",
245 "multiple statements on one line (semicolon)"),
246 "E703": QT_TRANSLATE_NOOP("pep8",
247 "statement ends with a semicolon"),
248 "E711": QT_TRANSLATE_NOOP("pep8",
249 "comparison to {0} should be {1}"),
250 "E712": QT_TRANSLATE_NOOP("pep8",
251 "comparison to {0} should be {1}"),
252 "E721": QT_TRANSLATE_NOOP("pep8",
253 "do not compare types, use 'isinstance()'"),
254 "E901": QT_TRANSLATE_NOOP("pep8",
255 "{0}: {1}"),
256 }
257
258 pep8_messages_sample_args = {
259 "E201": ["([{"],
260 "E202": ["}])"],
261 "E203": [",;:"],
262 "E211": ["(["],
263 "E231": [",;:"],
264 "E241": [",;:"],
265 "E242": [",;:"],
266 "E302": [1],
267 "E303": [3],
268 "E501": [85, 79],
269 "E711": ["None", "'if cond is None:'"],
270 "E712": ["True", "'if cond is True:' or 'if cond:'"],
271 "E901": ["SyntaxError", "Invalid Syntax"],
272 }
273
274
275 def getMessage(code, *args):
276 """
277 Function to get a translated and formatted message for a given code.
278
279 @param code message code (string)
280 @param args arguments for a formatted message (list)
281 @return translated and formatted message (string)
282 """
283 if code in pep8_messages:
284 return code + " " + QCoreApplication.translate("pep8",
285 pep8_messages[code]).format(*args)
286 else:
287 return code + " " + QCoreApplication.translate("pep8",
288 "no message for this code defined")
289
290 ##############################################################################
291 # Plugins (check functions) for physical lines
292 ##############################################################################
293
294
295 def tabs_or_spaces(physical_line, indent_char):
296 r"""
297 Never mix tabs and spaces.
298
299 The most popular way of indenting Python is with spaces only. The
300 second-most popular way is with tabs only. Code indented with a mixture
301 of tabs and spaces should be converted to using spaces exclusively. When
302 invoking the Python command line interpreter with the -t option, it issues
303 warnings about code that illegally mixes tabs and spaces. When using -tt
304 these warnings become errors. These options are highly recommended!
305
306 Okay: if a == 0:\n a = 1\n b = 1
307 E101: if a == 0:\n a = 1\n\tb = 1
308 """
309 indent = INDENT_REGEX.match(physical_line).group(1)
310 for offset, char in enumerate(indent):
311 if char != indent_char:
312 return offset, "E101"
313
314
315 def tabs_obsolete(physical_line):
316 r"""
317 For new projects, spaces-only are strongly recommended over tabs. Most
318 editors have features that make this easy to do.
319
320 Okay: if True:\n return
321 W191: if True:\n\treturn
322 """
323 indent = INDENT_REGEX.match(physical_line).group(1)
324 if '\t' in indent:
325 return indent.index('\t'), "W191"
326
327
328 def trailing_whitespace(physical_line):
329 r"""
330 JCR: Trailing whitespace is superfluous.
331 FBM: Except when it occurs as part of a blank line (i.e. the line is
332 nothing but whitespace). According to Python docs[1] a line with only
333 whitespace is considered a blank line, and is to be ignored. However,
334 matching a blank line to its indentation level avoids mistakenly
335 terminating a multi-line statement (e.g. class declaration) when
336 pasting code into the standard Python interpreter.
337
338 [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines
339
340 The warning returned varies on whether the line itself is blank, for easier
341 filtering for those who want to indent their blank lines.
342
343 Okay: spam(1)\n#
344 W291: spam(1) \n#
345 W293: class Foo(object):\n \n bang = 12
346 """
347 physical_line = physical_line.rstrip('\n') # chr(10), newline
348 physical_line = physical_line.rstrip('\r') # chr(13), carriage return
349 physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L
350 stripped = physical_line.rstrip(' \t\v')
351 if physical_line != stripped:
352 if stripped:
353 return len(stripped), "W291"
354 else:
355 return 0, "W293"
356
357
358 def trailing_blank_lines(physical_line, lines, line_number):
359 r"""
360 JCR: Trailing blank lines are superfluous.
361
362 Okay: spam(1)
363 W391: spam(1)\n
364 """
365 if not physical_line.rstrip() and line_number == len(lines):
366 return 0, "W391"
367
368
369 def missing_newline(physical_line):
370 """
371 JCR: The last line should have a newline.
372
373 Reports warning W292.
374 """
375 if physical_line.rstrip() == physical_line:
376 return len(physical_line), "W292"
377
378
379 def maximum_line_length(physical_line, max_line_length):
380 """
381 Limit all lines to a maximum of 79 characters.
382
383 There are still many devices around that are limited to 80 character
384 lines; plus, limiting windows to 80 characters makes it possible to have
385 several windows side-by-side. The default wrapping on such devices looks
386 ugly. Therefore, please limit all lines to a maximum of 79 characters.
387 For flowing long blocks of text (docstrings or comments), limiting the
388 length to 72 characters is recommended.
389
390 Reports error E501.
391 """
392 line = physical_line.rstrip()
393 length = len(line)
394 if length > max_line_length and not noqa(line):
395 if hasattr(line, 'decode'): # Python 2
396 # The line could contain multi-byte characters
397 try:
398 length = len(line.decode('utf-8'))
399 except (UnicodeDecodeError, UnicodeEncodeError):
400 pass
401 if length > max_line_length:
402 return max_line_length, "E501", length, max_line_length
403
404
405 ##############################################################################
406 # Plugins (check functions) for logical lines
407 ##############################################################################
408
409
410 def blank_lines(logical_line, blank_lines, indent_level, line_number,
411 previous_logical, previous_indent_level):
412 r"""
413 Separate top-level function and class definitions with two blank lines.
414
415 Method definitions inside a class are separated by a single blank line.
416
417 Extra blank lines may be used (sparingly) to separate groups of related
418 functions. Blank lines may be omitted between a bunch of related
419 one-liners (e.g. a set of dummy implementations).
420
421 Use blank lines in functions, sparingly, to indicate logical sections.
422
423 Okay: def a():\n pass\n\n\ndef b():\n pass
424 Okay: def a():\n pass\n\n\n# Foo\n# Bar\n\ndef b():\n pass
425
426 E301: class Foo:\n b = 0\n def bar():\n pass
427 E302: def a():\n pass\n\ndef b(n):\n pass
428 E303: def a():\n pass\n\n\n\ndef b(n):\n pass
429 E303: def a():\n\n\n\n pass
430 E304: @decorator\n\ndef a():\n pass
431 """
432 if line_number < 3 and not previous_logical:
433 return # Don't expect blank lines before the first line
434 if previous_logical.startswith('@'):
435 if blank_lines:
436 yield 0, "E304"
437 elif blank_lines > 2 or (indent_level and blank_lines == 2):
438 yield 0, "E303", blank_lines
439 elif logical_line.startswith(('def ', 'class ', '@')):
440 if indent_level:
441 if not (blank_lines or previous_indent_level < indent_level or
442 DOCSTRING_REGEX.match(previous_logical)):
443 yield 0, "E301"
444 elif blank_lines != 2:
445 yield 0, "E302", blank_lines
446
447
448 def extraneous_whitespace(logical_line):
449 """
450 Avoid extraneous whitespace in the following situations:
451
452 - Immediately inside parentheses, brackets or braces.
453
454 - Immediately before a comma, semicolon, or colon.
455
456 Okay: spam(ham[1], {eggs: 2})
457 E201: spam( ham[1], {eggs: 2})
458 E201: spam(ham[ 1], {eggs: 2})
459 E201: spam(ham[1], { eggs: 2})
460 E202: spam(ham[1], {eggs: 2} )
461 E202: spam(ham[1 ], {eggs: 2})
462 E202: spam(ham[1], {eggs: 2 })
463
464 E203: if x == 4: print x, y; x, y = y , x
465 E203: if x == 4: print x, y ; x, y = y, x
466 E203: if x == 4 : print x, y; x, y = y, x
467 """
468 line = logical_line
469 for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line):
470 text = match.group()
471 char = text.strip()
472 found = match.start()
473 if text == char + ' ':
474 # assert char in '([{'
475 yield found + 1, "E201", char
476 elif line[found - 1] != ',':
477 code = ('E202' if char in '}])' else 'E203') # if char in ',;:'
478 yield found, code, char
479
480
481 def whitespace_around_keywords(logical_line):
482 r"""
483 Avoid extraneous whitespace around keywords.
484
485 Okay: True and False
486 E271: True and False
487 E272: True and False
488 E273: True and\tFalse
489 E274: True\tand False
490 """
491 for match in KEYWORD_REGEX.finditer(logical_line):
492 before, after = match.groups()
493
494 if '\t' in before:
495 yield match.start(1), "E274"
496 elif len(before) > 1:
497 yield match.start(1), "E272"
498
499 if '\t' in after:
500 yield match.start(2), "E273"
501 elif len(after) > 1:
502 yield match.start(2), "E271"
503
504
505 def missing_whitespace(logical_line):
506 """
507 JCR: Each comma, semicolon or colon should be followed by whitespace.
508
509 Okay: [a, b]
510 Okay: (3,)
511 Okay: a[1:4]
512 Okay: a[:4]
513 Okay: a[1:]
514 Okay: a[1:4:2]
515 E231: ['a','b']
516 E231: foo(bar,baz)
517 E231: [{'a':'b'}]
518 """
519 line = logical_line
520 for index in range(len(line) - 1):
521 char = line[index]
522 if char in ',;:' and line[index + 1] not in WHITESPACE:
523 before = line[:index]
524 if char == ':' and before.count('[') > before.count(']') and \
525 before.rfind('{') < before.rfind('['):
526 continue # Slice syntax, no space required
527 if char == ',' and line[index + 1] == ')':
528 continue # Allow tuple with only one element: (3,)
529 yield index, "E231", char
530
531
532 def indentation(logical_line, previous_logical, indent_char,
533 indent_level, previous_indent_level):
534 r"""
535 Use 4 spaces per indentation level.
536
537 For really old code that you don't want to mess up, you can continue to
538 use 8-space tabs.
539
540 Okay: a = 1
541 Okay: if a == 0:\n a = 1
542 E111: a = 1
543
544 Okay: for item in items:\n pass
545 E112: for item in items:\npass
546
547 Okay: a = 1\nb = 2
548 E113: a = 1\n b = 2
549 """
550 if indent_char == ' ' and indent_level % 4:
551 yield 0, "E111"
552 indent_expect = previous_logical.endswith(':')
553 if indent_expect and indent_level <= previous_indent_level:
554 yield 0, "E112"
555 if indent_level > previous_indent_level and not indent_expect:
556 yield 0, "E113"
557
558
559 def continued_indentation(logical_line, tokens, indent_level, hang_closing,
560 noqa, verbose):
561 r"""
562 Continuation lines should align wrapped elements either vertically using
563 Python's implicit line joining inside parentheses, brackets and braces, or
564 using a hanging indent.
565
566 When using a hanging indent the following considerations should be applied:
567
568 - there should be no arguments on the first line, and
569
570 - further indentation should be used to clearly distinguish itself as a
571 continuation line.
572
573 Okay: a = (\n)
574 E123: a = (\n )
575
576 Okay: a = (\n 42)
577 E121: a = (\n 42)
578 E122: a = (\n42)
579 E123: a = (\n 42\n )
580 E124: a = (24,\n 42\n)
581 E125: if (a or\n b):\n pass
582 E126: a = (\n 42)
583 E127: a = (24,\n 42)
584 E128: a = (24,\n 42)
585 """
586 first_row = tokens[0][2][0]
587 nrows = 1 + tokens[-1][2][0] - first_row
588 if noqa or nrows == 1:
589 return
590
591 # indent_next tells us whether the next block is indented; assuming
592 # that it is indented by 4 spaces, then we should not allow 4-space
593 # indents on the final continuation line; in turn, some other
594 # indents are allowed to have an extra 4 spaces.
595 indent_next = logical_line.endswith(':')
596
597 row = depth = 0
598 # remember how many brackets were opened on each line
599 parens = [0] * nrows
600 # relative indents of physical lines
601 rel_indent = [0] * nrows
602 # visual indents
603 indent_chances = {}
604 last_indent = tokens[0][2]
605 indent = [last_indent[1]]
606 if verbose >= 3:
607 print(">>> " + tokens[0][4].rstrip())
608
609 for token_type, text, start, end, line in tokens:
610
611 last_token_multiline = (start[0] != end[0])
612 newline = row < start[0] - first_row
613 if newline:
614 row = start[0] - first_row
615 newline = (not last_token_multiline and
616 token_type not in (tokenize.NL, tokenize.NEWLINE))
617
618 if newline:
619 # this is the beginning of a continuation line.
620 last_indent = start
621 if verbose >= 3:
622 print("... " + line.rstrip())
623
624 # record the initial indent.
625 rel_indent[row] = expand_indent(line) - indent_level
626
627 if depth:
628 # a bracket expression in a continuation line.
629 # find the line that it was opened on
630 for open_row in range(row - 1, -1, -1):
631 if parens[open_row]:
632 break
633 else:
634 # an unbracketed continuation line (ie, backslash)
635 open_row = 0
636 hang = rel_indent[row] - rel_indent[open_row]
637 close_bracket = (token_type == tokenize.OP and text in ']})')
638 visual_indent = (not close_bracket and hang > 0 and
639 indent_chances.get(start[1]))
640
641 if close_bracket and indent[depth]:
642 # closing bracket for visual indent
643 if start[1] != indent[depth]:
644 yield start, "E124"
645 elif close_bracket and not hang:
646 # closing bracket matches indentation of opening bracket's line
647 if hang_closing:
648 yield start, "E133"
649 elif visual_indent is True:
650 # visual indent is verified
651 if not indent[depth]:
652 indent[depth] = start[1]
653 elif visual_indent in (text, str):
654 # ignore token lined up with matching one from a previous line
655 pass
656 elif indent[depth] and start[1] < indent[depth]:
657 # visual indent is broken
658 yield start, "E128"
659 elif hang == 4 or (indent_next and rel_indent[row] == 8):
660 # hanging indent is verified
661 if close_bracket and not hang_closing:
662 yield (start, "E123")
663 else:
664 # indent is broken
665 if hang <= 0:
666 error = "E122"
667 elif indent[depth]:
668 error = "E127"
669 elif hang % 4:
670 error = "E121"
671 else:
672 error = "E126"
673 yield start, error
674
675 # look for visual indenting
676 if (parens[row] and token_type not in (tokenize.NL, tokenize.COMMENT)
677 and not indent[depth]):
678 indent[depth] = start[1]
679 indent_chances[start[1]] = True
680 if verbose >= 4:
681 print("bracket depth %s indent to %s" % (depth, start[1]))
682 # deal with implicit string concatenation
683 elif (token_type in (tokenize.STRING, tokenize.COMMENT) or
684 text in ('u', 'ur', 'b', 'br')):
685 indent_chances[start[1]] = str
686 # special case for the "if" statement because len("if (") == 4
687 elif not indent_chances and not row and not depth and text == 'if':
688 indent_chances[end[1] + 1] = True
689
690 # keep track of bracket depth
691 if token_type == tokenize.OP:
692 if text in '([{':
693 depth += 1
694 indent.append(0)
695 parens[row] += 1
696 if verbose >= 4:
697 print("bracket depth %s seen, col %s, visual min = %s" %
698 (depth, start[1], indent[depth]))
699 elif text in ')]}' and depth > 0:
700 # parent indents should not be more than this one
701 prev_indent = indent.pop() or last_indent[1]
702 for d in range(depth):
703 if indent[d] > prev_indent:
704 indent[d] = 0
705 for ind in list(indent_chances):
706 if ind >= prev_indent:
707 del indent_chances[ind]
708 depth -= 1
709 if depth:
710 indent_chances[indent[depth]] = True
711 for idx in range(row, -1, -1):
712 if parens[idx]:
713 parens[idx] -= 1
714 rel_indent[row] = rel_indent[idx]
715 break
716 assert len(indent) == depth + 1
717 if start[1] not in indent_chances:
718 # allow to line up tokens
719 indent_chances[start[1]] = text
720
721 if indent_next and expand_indent(line) == indent_level + 4:
722 yield last_indent, "E125"
723
724
725 def whitespace_before_parameters(logical_line, tokens):
726 """
727 Avoid extraneous whitespace in the following situations:
728
729 - Immediately before the open parenthesis that starts the argument
730 list of a function call.
731
732 - Immediately before the open parenthesis that starts an indexing or
733 slicing.
734
735 Okay: spam(1)
736 E211: spam (1)
737
738 Okay: dict['key'] = list[index]
739 E211: dict ['key'] = list[index]
740 E211: dict['key'] = list [index]
741 """
742 prev_type, prev_text, __, prev_end, __ = tokens[0]
743 for index in range(1, len(tokens)):
744 token_type, text, start, end, __ = tokens[index]
745 if (token_type == tokenize.OP and
746 text in '([' and
747 start != prev_end and
748 (prev_type == tokenize.NAME or prev_text in '}])') and
749 # Syntax "class A (B):" is allowed, but avoid it
750 (index < 2 or tokens[index - 2][1] != 'class') and
751 # Allow "return (a.foo for a in range(5))"
752 not keyword.iskeyword(prev_text)):
753 yield prev_end, "E211", text
754 prev_type = token_type
755 prev_text = text
756 prev_end = end
757
758
759 def whitespace_around_operator(logical_line):
760 """
761 Avoid extraneous whitespace in the following situations:
762
763 - More than one space around an assignment (or other) operator to
764 align it with another.
765
766 Okay: a = 12 + 3
767 E221: a = 4 + 5
768 E222: a = 4 + 5
769 E223: a = 4\t+ 5
770 E224: a = 4 +\t5
771 """
772 for match in OPERATOR_REGEX.finditer(logical_line):
773 before, after = match.groups()
774
775 if '\t' in before:
776 yield match.start(1), "E223"
777 elif len(before) > 1:
778 yield match.start(1), "E221"
779
780 if '\t' in after:
781 yield match.start(2), "E224"
782 elif len(after) > 1:
783 yield match.start(2), "E222"
784
785 def missing_whitespace_around_operator(logical_line, tokens):
786 r"""
787 - Always surround these binary operators with a single space on
788 either side: assignment (=), augmented assignment (+=, -= etc.),
789 comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not),
790 Booleans (and, or, not).
791
792 - Use spaces around arithmetic operators.
793
794 Okay: i = i + 1
795 Okay: submitted += 1
796 Okay: x = x * 2 - 1
797 Okay: hypot2 = x * x + y * y
798 Okay: c = (a + b) * (a - b)
799 Okay: foo(bar, key='word', *args, **kwargs)
800 Okay: alpha[:-i]
801
802 E225: i=i+1
803 E225: submitted +=1
804 E225: x = x /2 - 1
805 E225: z = x **y
806 E226: c = (a+b) * (a-b)
807 E226: hypot2 = x*x + y*y
808 E227: c = a|b
809 E228: msg = fmt%(errno, errmsg)
810 """
811 parens = 0
812 need_space = False
813 prev_type = tokenize.OP
814 prev_text = prev_end = None
815 for token_type, text, start, end, line in tokens:
816 if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN):
817 # ERRORTOKEN is triggered by backticks in Python 3
818 continue
819 if text in ('(', 'lambda'):
820 parens += 1
821 elif text == ')':
822 parens -= 1
823 if need_space:
824 if start != prev_end:
825 # Found a (probably) needed space
826 if need_space is not True and not need_space[1]:
827 yield need_space[0], "E225"
828 need_space = False
829 elif text == '>' and prev_text in ('<', '-'):
830 # Tolerate the "<>" operator, even if running Python 3
831 # Deal with Python 3's annotated return value "->"
832 pass
833 else:
834 if need_space is True or need_space[1]:
835 # A needed trailing space was not found
836 yield prev_end, "E225"
837 else:
838 code = 'E226'
839 if prev_text == '%':
840 code = 'E228'
841 elif prev_text not in ARITHMETIC_OP:
842 code = 'E227'
843 yield need_space[0], code
844 need_space = False
845 elif token_type == tokenize.OP and prev_end is not None:
846 if text == '=' and parens:
847 # Allow keyword args or defaults: foo(bar=None).
848 pass
849 elif text in WS_NEEDED_OPERATORS:
850 need_space = True
851 elif text in UNARY_OPERATORS:
852 # Check if the operator is being used as a binary operator
853 # Allow unary operators: -123, -x, +1.
854 # Allow argument unpacking: foo(*args, **kwargs).
855 if prev_type == tokenize.OP:
856 binary_usage = (prev_text in '}])')
857 elif prev_type == tokenize.NAME:
858 binary_usage = (prev_text not in KEYWORDS)
859 else:
860 binary_usage = (prev_type not in SKIP_TOKENS)
861
862 if binary_usage:
863 need_space = None
864 elif text in WS_OPTIONAL_OPERATORS:
865 need_space = None
866
867 if need_space is None:
868 # Surrounding space is optional, but ensure that
869 # trailing space matches opening space
870 need_space = (prev_end, start != prev_end)
871 elif need_space and start == prev_end:
872 # A needed opening space was not found
873 yield prev_end, "E225"
874 need_space = False
875 prev_type = token_type
876 prev_text = text
877 prev_end = end
878
879
880 def whitespace_around_comma(logical_line):
881 """
882 Avoid extraneous whitespace in the following situations:
883
884 - More than one space around an assignment (or other) operator to
885 align it with another.
886
887 JCR: This should also be applied around comma etc.
888 Note: these checks are disabled by default
889
890 Okay: a = (1, 2)
891 E241: a = (1, 2)
892 E242: a = (1,\t2)
893 """
894 line = logical_line
895 for m in WHITESPACE_AFTER_COMMA_REGEX.finditer(line):
896 found = m.start() + 1
897 if '\t' in m.group():
898 yield found, "E242", m.group()[0]
899 else:
900 yield found, "E241", m.group()[0]
901
902
903 def whitespace_around_named_parameter_equals(logical_line, tokens):
904 """
905 Don't use spaces around the '=' sign when used to indicate a
906 keyword argument or a default parameter value.
907
908 Okay: def complex(real, imag=0.0):
909 Okay: return magic(r=real, i=imag)
910 Okay: boolean(a == b)
911 Okay: boolean(a != b)
912 Okay: boolean(a <= b)
913 Okay: boolean(a >= b)
914
915 E251: def complex(real, imag = 0.0):
916 E251: return magic(r = real, i = imag)
917 """
918 parens = 0
919 no_space = False
920 prev_end = None
921 message = "E251"
922 for token_type, text, start, end, line in tokens:
923 if no_space:
924 no_space = False
925 if start != prev_end:
926 yield prev_end, message
927 elif token_type == tokenize.OP:
928 if text == '(':
929 parens += 1
930 elif text == ')':
931 parens -= 1
932 elif parens and text == '=':
933 no_space = True
934 if start != prev_end:
935 yield prev_end, message
936 prev_end = end
937
938
939 def whitespace_before_inline_comment(logical_line, tokens):
940 """
941 Separate inline comments by at least two spaces.
942
943 An inline comment is a comment on the same line as a statement. Inline
944 comments should be separated by at least two spaces from the statement.
945 They should start with a # and a single space.
946
947 Okay: x = x + 1 # Increment x
948 Okay: x = x + 1 # Increment x
949 E261: x = x + 1 # Increment x
950 E262: x = x + 1 #Increment x
951 E262: x = x + 1 # Increment x
952 """
953 prev_end = (0, 0)
954 for token_type, text, start, end, line in tokens:
955 if token_type == tokenize.COMMENT:
956 if not line[:start[1]].strip():
957 continue
958 if prev_end[0] == start[0] and start[1] < prev_end[1] + 2:
959 yield prev_end, "E261"
960 symbol, sp, comment = text.partition(' ')
961 if symbol not in ('#', '#:') or comment[:1].isspace():
962 yield start, "E262"
963 elif token_type != tokenize.NL:
964 prev_end = end
965
966
967 def imports_on_separate_lines(logical_line):
968 r"""
969 Imports should usually be on separate lines.
970
971 Okay: import os\nimport sys
972 E401: import sys, os
973
974 Okay: from subprocess import Popen, PIPE
975 Okay: from myclas import MyClass
976 Okay: from foo.bar.yourclass import YourClass
977 Okay: import myclass
978 Okay: import foo.bar.yourclass
979 """
980 line = logical_line
981 if line.startswith('import '):
982 found = line.find(',')
983 if -1 < found and ';' not in line[:found]:
984 yield found, "E401"
985
986
987 def compound_statements(logical_line):
988 r"""
989 Compound statements (multiple statements on the same line) are
990 generally discouraged.
991
992 While sometimes it's okay to put an if/for/while with a small body
993 on the same line, never do this for multi-clause statements. Also
994 avoid folding such long lines!
995
996 Okay: if foo == 'blah':\n do_blah_thing()
997 Okay: do_one()
998 Okay: do_two()
999 Okay: do_three()
1000
1001 E701: if foo == 'blah': do_blah_thing()
1002 E701: for x in lst: total += x
1003 E701: while t < 10: t = delay()
1004 E701: if foo == 'blah': do_blah_thing()
1005 E701: else: do_non_blah_thing()
1006 E701: try: something()
1007 E701: finally: cleanup()
1008 E701: if foo == 'blah': one(); two(); three()
1009
1010 E702: do_one(); do_two(); do_three()
1011 E703: do_four(); # useless semicolon
1012 """
1013 line = logical_line
1014 last_char = len(line) - 1
1015 found = line.find(':')
1016 while -1 < found < last_char:
1017 before = line[:found]
1018 if (before.count('{') <= before.count('}') and # {'a': 1} (dict)
1019 before.count('[') <= before.count(']') and # [1:2] (slice)
1020 before.count('(') <= before.count(')') and # (Python 3 annotation)
1021 not LAMBDA_REGEX.search(before)): # lambda x: x
1022 yield found, "E701"
1023 found = line.find(':', found + 1)
1024 found = line.find(';')
1025 while -1 < found:
1026 if found < last_char:
1027 yield found, "E702"
1028 else:
1029 yield found, "E703"
1030 found = line.find(';', found + 1)
1031
1032
1033 def explicit_line_join(logical_line, tokens):
1034 r"""
1035 Avoid explicit line join between brackets.
1036
1037 The preferred way of wrapping long lines is by using Python's implied line
1038 continuation inside parentheses, brackets and braces. Long lines can be
1039 broken over multiple lines by wrapping expressions in parentheses. These
1040 should be used in preference to using a backslash for line continuation.
1041
1042 E502: aaa = [123, \\n 123]
1043 E502: aaa = ("bbb " \\n "ccc")
1044
1045 Okay: aaa = [123,\n 123]
1046 Okay: aaa = ("bbb "\n "ccc")
1047 Okay: aaa = "bbb " \\n "ccc"
1048 """
1049 prev_start = prev_end = parens = 0
1050 backslash = None
1051 for token_type, text, start, end, line in tokens:
1052 if start[0] != prev_start and parens and backslash:
1053 yield backslash, "E502"
1054 if end[0] != prev_end:
1055 if line.rstrip('\r\n').endswith('\\'):
1056 backslash = (end[0], len(line.splitlines()[-1]) - 1)
1057 else:
1058 backslash = None
1059 prev_start = prev_end = end[0]
1060 else:
1061 prev_start = start[0]
1062 if token_type == tokenize.OP:
1063 if text in '([{':
1064 parens += 1
1065 elif text in ')]}':
1066 parens -= 1
1067
1068
1069 def comparison_to_singleton(logical_line, noqa):
1070 """
1071 Comparisons to singletons like None should always be done
1072 with "is" or "is not", never the equality operators.
1073
1074 Okay: if arg is not None:
1075 E711: if arg != None:
1076 E712: if arg == True:
1077
1078 Also, beware of writing if x when you really mean if x is not None --
1079 e.g. when testing whether a variable or argument that defaults to None was
1080 set to some other value. The other value might have a type (such as a
1081 container) that could be false in a boolean context!
1082 """
1083 match = not noqa and COMPARE_SINGLETON_REGEX.search(logical_line)
1084 if match:
1085 same = (match.group(1) == '==')
1086 singleton = match.group(2)
1087 msg = "'if cond is %s:'" % (('' if same else 'not ') + singleton)
1088 if singleton in ('None',):
1089 code = 'E711'
1090 else:
1091 code = 'E712'
1092 nonzero = ((singleton == 'True' and same) or
1093 (singleton == 'False' and not same))
1094 msg += " or 'if %scond:'" % ('' if nonzero else 'not ')
1095 yield match.start(1), code, singleton, msg
1096
1097
1098 def comparison_type(logical_line):
1099 """
1100 Object type comparisons should always use isinstance() instead of
1101 comparing types directly.
1102
1103 Okay: if isinstance(obj, int):
1104 E721: if type(obj) is type(1):
1105
1106 When checking if an object is a string, keep in mind that it might be a
1107 unicode string too! In Python 2.3, str and unicode have a common base
1108 class, basestring, so you can do:
1109
1110 Okay: if isinstance(obj, basestring):
1111 Okay: if type(a1) is type(b1):
1112 """
1113 match = COMPARE_TYPE_REGEX.search(logical_line)
1114 if match:
1115 inst = match.group(1)
1116 if inst and isidentifier(inst) and inst not in SINGLETONS:
1117 return # Allow comparison for types which are not obvious
1118 yield match.start(), "E721"
1119
1120
1121 def python_3000_has_key(logical_line):
1122 r"""
1123 The {}.has_key() method is removed in the Python 3.
1124 Use the 'in' operation instead.
1125
1126 Okay: if "alph" in d:\n print d["alph"]
1127 W601: assert d.has_key('alph')
1128 """
1129 pos = logical_line.find('.has_key(')
1130 if pos > -1:
1131 yield pos, "W601"
1132
1133
1134 def python_3000_raise_comma(logical_line):
1135 """
1136 When raising an exception, use "raise ValueError('message')"
1137 instead of the older form "raise ValueError, 'message'".
1138
1139 The paren-using form is preferred because when the exception arguments
1140 are long or include string formatting, you don't need to use line
1141 continuation characters thanks to the containing parentheses. The older
1142 form is removed in Python 3.
1143
1144 Okay: raise DummyError("Message")
1145 W602: raise DummyError, "Message"
1146 """
1147 match = RAISE_COMMA_REGEX.match(logical_line)
1148 if match and not RERAISE_COMMA_REGEX.match(logical_line):
1149 yield match.end() - 1, "W602"
1150
1151
1152 def python_3000_not_equal(logical_line):
1153 """
1154 != can also be written <>, but this is an obsolete usage kept for
1155 backwards compatibility only. New code should always use !=.
1156 The older syntax is removed in Python 3.
1157
1158 Okay: if a != 'no':
1159 W603: if a <> 'no':
1160 """
1161 pos = logical_line.find('<>')
1162 if pos > -1:
1163 yield pos, "W603"
1164
1165
1166 def python_3000_backticks(logical_line):
1167 """
1168 Backticks are removed in Python 3.
1169 Use repr() instead.
1170
1171 Okay: val = repr(1 + 2)
1172 W604: val = `1 + 2`
1173 """
1174 pos = logical_line.find('`')
1175 if pos > -1:
1176 yield pos, "W604"
1177
1178
1179 ##############################################################################
1180 # Helper functions
1181 ##############################################################################
1182
1183
1184 if '' == ''.encode():
1185 # Python 2: implicit encoding.
1186 def readlines(filename):
1187 f = open(filename)
1188 try:
1189 return f.readlines()
1190 finally:
1191 f.close()
1192 isidentifier = re.compile(r'[a-zA-Z_]\w*').match
1193 stdin_get_value = sys.stdin.read
1194 else:
1195 # Python 3
1196 def readlines(filename): # __IGNORE_WARNING__
1197 f = open(filename, 'rb')
1198 try:
1199 coding, lines = tokenize.detect_encoding(f.readline)
1200 f = TextIOWrapper(f, coding, line_buffering=True)
1201 return [l.decode(coding) for l in lines] + f.readlines()
1202 except (LookupError, SyntaxError, UnicodeError):
1203 f.close()
1204 # Fall back if files are improperly declared
1205 f = open(filename, encoding='latin-1')
1206 return f.readlines()
1207 finally:
1208 f.close()
1209 isidentifier = str.isidentifier
1210
1211 def stdin_get_value():
1212 return TextIOWrapper(sys.stdin.buffer, errors='ignore').read()
1213 readlines.__doc__ = " Read the source code."
1214 noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search
1215
1216
1217 def expand_indent(line):
1218 r"""
1219 Return the amount of indentation.
1220 Tabs are expanded to the next multiple of 8.
1221
1222 >>> expand_indent(' ')
1223 4
1224 >>> expand_indent('\t')
1225 8
1226 >>> expand_indent(' \t')
1227 8
1228 >>> expand_indent(' \t')
1229 8
1230 >>> expand_indent(' \t')
1231 16
1232 """
1233 if '\t' not in line:
1234 return len(line) - len(line.lstrip())
1235 result = 0
1236 for char in line:
1237 if char == '\t':
1238 result = result // 8 * 8 + 8
1239 elif char == ' ':
1240 result += 1
1241 else:
1242 break
1243 return result
1244
1245
1246 def mute_string(text):
1247 """
1248 Replace contents with 'xxx' to prevent syntax matching.
1249
1250 >>> mute_string('"abc"')
1251 '"xxx"'
1252 >>> mute_string("'''abc'''")
1253 "'''xxx'''"
1254 >>> mute_string("r'abc'")
1255 "r'xxx'"
1256 """
1257 # String modifiers (e.g. u or r)
1258 start = text.index(text[-1]) + 1
1259 end = len(text) - 1
1260 # Triple quotes
1261 if text[-3:] in ('"""', "'''"):
1262 start += 2
1263 end -= 2
1264 return text[:start] + 'x' * (end - start) + text[end:]
1265
1266
1267 def parse_udiff(diff, patterns=None, parent='.'):
1268 """Return a dictionary of matching lines."""
1269 # For each file of the diff, the entry key is the filename,
1270 # and the value is a set of row numbers to consider.
1271 rv = {}
1272 path = nrows = None
1273 for line in diff.splitlines():
1274 if nrows:
1275 if line[:1] != '-':
1276 nrows -= 1
1277 continue
1278 if line[:3] == '@@ ':
1279 hunk_match = HUNK_REGEX.match(line)
1280 row, nrows = [int(g or '1') for g in hunk_match.groups()]
1281 rv[path].update(range(row, row + nrows))
1282 elif line[:3] == '+++':
1283 path = line[4:].split('\t', 1)[0]
1284 if path[:2] == 'b/':
1285 path = path[2:]
1286 rv[path] = set()
1287 return dict([(os.path.join(parent, path), rows)
1288 for (path, rows) in rv.items()
1289 if rows and filename_match(path, patterns)])
1290
1291
1292 def filename_match(filename, patterns, default=True):
1293 """
1294 Check if patterns contains a pattern that matches filename.
1295 If patterns is unspecified, this always returns True.
1296 """
1297 if not patterns:
1298 return default
1299 return any(fnmatch(filename, pattern) for pattern in patterns)
1300
1301
1302 ##############################################################################
1303 # Framework to run all checks
1304 ##############################################################################
1305
1306
1307 _checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}}
1308
1309
1310 def register_check(check, codes=None):
1311 """
1312 Register a new check object.
1313 """
1314 def _add_check(check, kind, codes, args):
1315 if check in _checks[kind]:
1316 _checks[kind][check][0].extend(codes or [])
1317 else:
1318 _checks[kind][check] = (codes or [''], args)
1319 if inspect.isfunction(check):
1320 args = inspect.getargspec(check)[0]
1321 if args and args[0] in ('physical_line', 'logical_line'):
1322 if codes is None:
1323 codes = ERRORCODE_REGEX.findall(check.__doc__ or '')
1324 _add_check(check, args[0], codes, args)
1325 elif inspect.isclass(check):
1326 if inspect.getargspec(check.__init__)[0][:2] == ['self', 'tree']:
1327 _add_check(check, 'tree', codes, None)
1328
1329
1330 def init_checks_registry():
1331 """
1332 Register all globally visible functions where the first argument name
1333 is 'physical_line' or 'logical_line'.
1334 """
1335 mod = inspect.getmodule(register_check)
1336 for (name, function) in inspect.getmembers(mod, inspect.isfunction):
1337 register_check(function)
1338 init_checks_registry()
1339
1340
1341 class Checker(object):
1342 """
1343 Load a Python source file, tokenize it, check coding style.
1344 """
1345
1346 def __init__(self, filename=None, lines=None,
1347 options=None, report=None, **kwargs):
1348 if options is None:
1349 options = StyleGuide(kwargs).options
1350 else:
1351 assert not kwargs
1352 self._io_error = None
1353 self._physical_checks = options.physical_checks
1354 self._logical_checks = options.logical_checks
1355 self._ast_checks = options.ast_checks
1356 self.max_line_length = options.max_line_length
1357 self.hang_closing = options.hang_closing
1358 self.verbose = options.verbose
1359 self.filename = filename
1360 if filename is None:
1361 self.filename = 'stdin'
1362 self.lines = lines or []
1363 elif filename == '-':
1364 self.filename = 'stdin'
1365 self.lines = stdin_get_value().splitlines(True)
1366 elif lines is None:
1367 try:
1368 self.lines = readlines(filename)
1369 except IOError:
1370 exc_type, exc = sys.exc_info()[:2]
1371 self._io_error = '%s: %s' % (exc_type.__name__, exc)
1372 self.lines = []
1373 else:
1374 self.lines = lines
1375 if self.lines:
1376 ord0 = ord(self.lines[0][0])
1377 if ord0 in (0xef, 0xfeff): # Strip the UTF-8 BOM
1378 if ord0 == 0xfeff:
1379 self.lines[0] = self.lines[0][1:]
1380 elif self.lines[0][:3] == '\xef\xbb\xbf':
1381 self.lines[0] = self.lines[0][3:]
1382 self.report = report or options.report
1383 self.report_error = self.report.error
1384 self.report_error_args = self.report.error_args
1385
1386 # added for eric5 integration
1387 self.options = options
1388
1389 def report_invalid_syntax(self):
1390 exc_type, exc = sys.exc_info()[:2]
1391 if len(exc.args) > 1:
1392 offset = exc.args[1]
1393 if len(offset) > 2:
1394 offset = offset[1:3]
1395 else:
1396 offset = (1, 0)
1397 self.report_error_args(offset[0], offset[1] or 0,
1398 'E901', self.report_invalid_syntax,
1399 exc_type.__name__, exc.args[0])
1400 report_invalid_syntax.__doc__ = " Check if the syntax is valid."
1401
1402 def readline(self):
1403 """
1404 Get the next line from the input buffer.
1405 """
1406 self.line_number += 1
1407 if self.line_number > len(self.lines):
1408 return ''
1409 return self.lines[self.line_number - 1]
1410
1411 def readline_check_physical(self):
1412 """
1413 Check and return the next physical line. This method can be
1414 used to feed tokenize.generate_tokens.
1415 """
1416 line = self.readline()
1417 if line:
1418 self.check_physical(line)
1419 return line
1420
1421 def run_check(self, check, argument_names):
1422 """
1423 Run a check plugin.
1424 """
1425 arguments = []
1426 for name in argument_names:
1427 arguments.append(getattr(self, name))
1428 return check(*arguments)
1429
1430 def check_physical(self, line):
1431 """
1432 Run all physical checks on a raw input line.
1433 """
1434 self.physical_line = line
1435 if self.indent_char is None and line[:1] in WHITESPACE:
1436 self.indent_char = line[0]
1437 for name, check, argument_names in self._physical_checks:
1438 result = self.run_check(check, argument_names)
1439 if result is not None:
1440 offset, code = result[:2]
1441 args = result[2:]
1442 self.report_error_args(self.line_number, offset, code, check,
1443 *args)
1444
1445 def build_tokens_line(self):
1446 """
1447 Build a logical line from tokens.
1448 """
1449 self.mapping = []
1450 logical = []
1451 comments = []
1452 length = 0
1453 previous = None
1454 for token in self.tokens:
1455 token_type, text = token[0:2]
1456 if token_type == tokenize.COMMENT:
1457 comments.append(text)
1458 continue
1459 if token_type in SKIP_TOKENS:
1460 continue
1461 if token_type == tokenize.STRING:
1462 text = mute_string(text)
1463 if previous:
1464 end_row, end = previous[3]
1465 start_row, start = token[2]
1466 if end_row != start_row: # different row
1467 prev_text = self.lines[end_row - 1][end - 1]
1468 if prev_text == ',' or (prev_text not in '{[('
1469 and text not in '}])'):
1470 logical.append(' ')
1471 length += 1
1472 elif end != start: # different column
1473 fill = self.lines[end_row - 1][end:start]
1474 logical.append(fill)
1475 length += len(fill)
1476 self.mapping.append((length, token))
1477 logical.append(text)
1478 length += len(text)
1479 previous = token
1480 self.logical_line = ''.join(logical)
1481 self.noqa = comments and noqa(''.join(comments))
1482 # With Python 2, if the line ends with '\r\r\n' the assertion fails
1483 # assert self.logical_line.strip() == self.logical_line
1484
1485 def check_logical(self):
1486 """
1487 Build a line from tokens and run all logical checks on it.
1488 """
1489 self.build_tokens_line()
1490 self.report.increment_logical_line()
1491 first_line = self.lines[self.mapping[0][1][2][0] - 1]
1492 indent = first_line[:self.mapping[0][1][2][1]]
1493 self.previous_indent_level = self.indent_level
1494 self.indent_level = expand_indent(indent)
1495 if self.verbose >= 2:
1496 print(self.logical_line[:80].rstrip())
1497 for name, check, argument_names in self._logical_checks:
1498 if self.verbose >= 4:
1499 print(' ' + name)
1500 for result in self.run_check(check, argument_names):
1501 offset, code = result[:2]
1502 args = result[2:]
1503 if isinstance(offset, tuple):
1504 orig_number, orig_offset = offset
1505 else:
1506 for token_offset, token in self.mapping:
1507 if offset >= token_offset:
1508 orig_number = token[2][0]
1509 orig_offset = (token[2][1] + offset - token_offset)
1510 self.report_error_args(orig_number, orig_offset, code, check,
1511 *args)
1512 self.previous_logical = self.logical_line
1513
1514 def check_ast(self):
1515 try:
1516 tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST)
1517 except (SyntaxError, TypeError):
1518 return self.report_invalid_syntax()
1519 for name, cls, _ in self._ast_checks:
1520 # extended API for eric5 integration
1521 checker = cls(tree, self.filename, self.options)
1522 for args in checker.run():
1523 lineno = args[0]
1524 if not noqa(self.lines[lineno - 1]):
1525 self.report_error_args(lineno, *args[1:])
1526
1527 def generate_tokens(self):
1528 if self._io_error:
1529 self.report_error(1, 0, 'E902 %s' % self._io_error, readlines)
1530 tokengen = tokenize.generate_tokens(self.readline_check_physical)
1531 try:
1532 for token in tokengen:
1533 yield token
1534 except (SyntaxError, tokenize.TokenError):
1535 self.report_invalid_syntax()
1536
1537 def check_all(self, expected=None, line_offset=0):
1538 """
1539 Run all checks on the input file.
1540 """
1541 self.report.init_file(self.filename, self.lines, expected, line_offset)
1542 if self._ast_checks:
1543 self.check_ast()
1544 self.line_number = 0
1545 self.indent_char = None
1546 self.indent_level = 0
1547 self.previous_logical = ''
1548 self.tokens = []
1549 self.blank_lines = blank_lines_before_comment = 0
1550 parens = 0
1551 for token in self.generate_tokens():
1552 self.tokens.append(token)
1553 token_type, text = token[0:2]
1554 if self.verbose >= 3:
1555 if token[2][0] == token[3][0]:
1556 pos = '[%s:%s]' % (token[2][1] or '', token[3][1])
1557 else:
1558 pos = 'l.%s' % token[3][0]
1559 print('l.%s\t%s\t%s\t%r' %
1560 (token[2][0], pos, tokenize.tok_name[token[0]], text))
1561 if token_type == tokenize.OP:
1562 if text in '([{':
1563 parens += 1
1564 elif text in '}])':
1565 parens -= 1
1566 elif not parens:
1567 if token_type == tokenize.NEWLINE:
1568 if self.blank_lines < blank_lines_before_comment:
1569 self.blank_lines = blank_lines_before_comment
1570 self.check_logical()
1571 self.tokens = []
1572 self.blank_lines = blank_lines_before_comment = 0
1573 elif token_type == tokenize.NL:
1574 if len(self.tokens) == 1:
1575 # The physical line contains only this token.
1576 self.blank_lines += 1
1577 self.tokens = []
1578 elif token_type == tokenize.COMMENT and len(self.tokens) == 1:
1579 if blank_lines_before_comment < self.blank_lines:
1580 blank_lines_before_comment = self.blank_lines
1581 self.blank_lines = 0
1582 if COMMENT_WITH_NL:
1583 # The comment also ends a physical line
1584 self.tokens = []
1585 return self.report.get_file_results()
1586
1587
1588 class BaseReport(object):
1589 """Collect the results of the checks."""
1590 print_filename = False
1591
1592 def __init__(self, options):
1593 self._benchmark_keys = options.benchmark_keys
1594 self._ignore_code = options.ignore_code
1595 # Results
1596 self.elapsed = 0
1597 self.total_errors = 0
1598 self.counters = dict.fromkeys(self._benchmark_keys, 0)
1599 self.messages = {}
1600
1601 def start(self):
1602 """Start the timer."""
1603 self._start_time = time.time()
1604
1605 def stop(self):
1606 """Stop the timer."""
1607 self.elapsed = time.time() - self._start_time
1608
1609 def init_file(self, filename, lines, expected, line_offset):
1610 """Signal a new file."""
1611 self.filename = filename
1612 self.lines = lines
1613 self.expected = expected or ()
1614 self.line_offset = line_offset
1615 self.file_errors = 0
1616 self.counters['files'] += 1
1617 self.counters['physical lines'] += len(lines)
1618
1619 def increment_logical_line(self):
1620 """Signal a new logical line."""
1621 self.counters['logical lines'] += 1
1622
1623 def error(self, line_number, offset, text, check):
1624 """Report an error, according to options."""
1625 code = text[:4]
1626 if self._ignore_code(code):
1627 return
1628 if code in self.counters:
1629 self.counters[code] += 1
1630 else:
1631 self.counters[code] = 1
1632 self.messages[code] = text[5:]
1633 # Don't care about expected errors or warnings
1634 if code in self.expected:
1635 return
1636 if self.print_filename and not self.file_errors:
1637 print(self.filename)
1638 self.file_errors += 1
1639 self.total_errors += 1
1640 return code
1641
1642 def error_args(self, line_number, offset, code, check, *args):
1643 """Report an error, according to options."""
1644 if self._ignore_code(code):
1645 return
1646 text = getMessage(code, *args)
1647 if code in self.counters:
1648 self.counters[code] += 1
1649 else:
1650 self.counters[code] = 1
1651 self.messages[code] = text[5:]
1652 # Don't care about expected errors or warnings
1653 if code in self.expected:
1654 return
1655 if self.print_filename and not self.file_errors:
1656 print(self.filename)
1657 self.file_errors += 1
1658 self.total_errors += 1
1659 return code
1660
1661 def get_file_results(self):
1662 """Return the count of errors and warnings for this file."""
1663 return self.file_errors
1664
1665 def get_count(self, prefix=''):
1666 """Return the total count of errors and warnings."""
1667 return sum([self.counters[key]
1668 for key in self.messages if key.startswith(prefix)])
1669
1670 def get_statistics(self, prefix=''):
1671 """
1672 Get statistics for message codes that start with the prefix.
1673
1674 prefix='' matches all errors and warnings
1675 prefix='E' matches all errors
1676 prefix='W' matches all warnings
1677 prefix='E4' matches all errors that have to do with imports
1678 """
1679 return ['%-7s %s %s' % (self.counters[key], key, self.messages[key])
1680 for key in sorted(self.messages) if key.startswith(prefix)]
1681
1682 def print_statistics(self, prefix=''):
1683 """Print overall statistics (number of errors and warnings)."""
1684 for line in self.get_statistics(prefix):
1685 print(line)
1686
1687 def print_benchmark(self):
1688 """Print benchmark numbers."""
1689 print('%-7.2f %s' % (self.elapsed, 'seconds elapsed'))
1690 if self.elapsed:
1691 for key in self._benchmark_keys:
1692 print('%-7d %s per second (%d total)' %
1693 (self.counters[key] / self.elapsed, key,
1694 self.counters[key]))
1695
1696
1697 class FileReport(BaseReport):
1698 """Collect the results of the checks and print only the filenames."""
1699 print_filename = True
1700
1701
1702 class StandardReport(BaseReport):
1703 """Collect and print the results of the checks."""
1704
1705 def __init__(self, options):
1706 super(StandardReport, self).__init__(options)
1707 self._fmt = REPORT_FORMAT.get(options.format.lower(),
1708 options.format)
1709 self._repeat = options.repeat
1710 self._show_source = options.show_source
1711 self._show_pep8 = options.show_pep8
1712
1713 def init_file(self, filename, lines, expected, line_offset):
1714 """Signal a new file."""
1715 self._deferred_print = []
1716 return super(StandardReport, self).init_file(
1717 filename, lines, expected, line_offset)
1718
1719 def error(self, line_number, offset, text, check):
1720 """Report an error, according to options."""
1721 code = super(StandardReport, self).error(line_number, offset,
1722 text, check)
1723 if code and (self.counters[code] == 1 or self._repeat):
1724 self._deferred_print.append(
1725 (line_number, offset, code, text[5:], check.__doc__))
1726 return code
1727
1728 def error_args(self, line_number, offset, code, check, *args):
1729 """Report an error, according to options."""
1730 code = super(StandardReport, self).error_args(line_number, offset,
1731 code, check, *args)
1732 if code and (self.counters[code] == 1 or self._repeat):
1733 text = getMessage(code, *args)
1734 self._deferred_print.append(
1735 (line_number, offset, code, text[5:], check.__doc__))
1736 return code
1737
1738 def get_file_results(self):
1739 """Print the result and return the overall count for this file."""
1740 self._deferred_print.sort()
1741 for line_number, offset, code, text, doc in self._deferred_print:
1742 print(self._fmt % {
1743 'path': self.filename,
1744 'row': self.line_offset + line_number, 'col': offset + 1,
1745 'code': code, 'text': text,
1746 })
1747 if self._show_source:
1748 if line_number > len(self.lines):
1749 line = ''
1750 else:
1751 line = self.lines[line_number - 1]
1752 print(line.rstrip())
1753 print(' ' * offset + '^')
1754 if self._show_pep8 and doc:
1755 print(doc.lstrip('\n').rstrip())
1756 return self.file_errors
1757
1758
1759 class DiffReport(StandardReport):
1760 """Collect and print the results for the changed lines only."""
1761
1762 def __init__(self, options):
1763 super(DiffReport, self).__init__(options)
1764 self._selected = options.selected_lines
1765
1766 def error(self, line_number, offset, text, check):
1767 if line_number not in self._selected[self.filename]:
1768 return
1769 return super(DiffReport, self).error(line_number, offset, text, check)
1770
1771
1772 class StyleGuide(object):
1773 """Initialize a PEP-8 instance with few options."""
1774
1775 def __init__(self, *args, **kwargs):
1776 # build options from the command line
1777 self.checker_class = kwargs.pop('checker_class', Checker)
1778 parse_argv = kwargs.pop('parse_argv', False)
1779 config_file = kwargs.pop('config_file', None)
1780 parser = kwargs.pop('parser', None)
1781 options, self.paths = process_options(
1782 parse_argv=parse_argv, config_file=config_file, parser=parser)
1783 if args or kwargs:
1784 # build options from dict
1785 options_dict = dict(*args, **kwargs)
1786 options.__dict__.update(options_dict)
1787 if 'paths' in options_dict:
1788 self.paths = options_dict['paths']
1789
1790 self.runner = self.input_file
1791 self.options = options
1792
1793 if not options.reporter:
1794 options.reporter = BaseReport if options.quiet else StandardReport
1795
1796 for index, value in enumerate(options.exclude):
1797 options.exclude[index] = value.rstrip('/')
1798 options.select = tuple(options.select or ())
1799 if not (options.select or options.ignore or
1800 options.testsuite or options.doctest) and DEFAULT_IGNORE:
1801 # The default choice: ignore controversial checks
1802 options.ignore = tuple(DEFAULT_IGNORE.split(','))
1803 else:
1804 # Ignore all checks which are not explicitly selected
1805 options.ignore = ('',) if options.select else tuple(options.ignore)
1806 options.benchmark_keys = BENCHMARK_KEYS[:]
1807 options.ignore_code = self.ignore_code
1808 options.physical_checks = self.get_checks('physical_line')
1809 options.logical_checks = self.get_checks('logical_line')
1810 options.ast_checks = self.get_checks('tree')
1811 self.init_report()
1812
1813 def init_report(self, reporter=None):
1814 """Initialize the report instance."""
1815 self.options.report = (reporter or self.options.reporter)(self.options)
1816 return self.options.report
1817
1818 def check_files(self, paths=None):
1819 """Run all checks on the paths."""
1820 if paths is None:
1821 paths = self.paths
1822 report = self.options.report
1823 runner = self.runner
1824 report.start()
1825 try:
1826 for path in paths:
1827 if os.path.isdir(path):
1828 self.input_dir(path)
1829 elif not self.excluded(path):
1830 runner(path)
1831 except KeyboardInterrupt:
1832 print('... stopped')
1833 report.stop()
1834 return report
1835
1836 def input_file(self, filename, lines=None, expected=None, line_offset=0):
1837 """Run all checks on a Python source file."""
1838 if self.options.verbose:
1839 print('checking %s' % filename)
1840 fchecker = self.checker_class(
1841 filename, lines=lines, options=self.options)
1842 return fchecker.check_all(expected=expected, line_offset=line_offset)
1843
1844 def input_dir(self, dirname):
1845 """Check all files in this directory and all subdirectories."""
1846 dirname = dirname.rstrip('/')
1847 if self.excluded(dirname):
1848 return 0
1849 counters = self.options.report.counters
1850 verbose = self.options.verbose
1851 filepatterns = self.options.filename
1852 runner = self.runner
1853 for root, dirs, files in os.walk(dirname):
1854 if verbose:
1855 print('directory ' + root)
1856 counters['directories'] += 1
1857 for subdir in sorted(dirs):
1858 if self.excluded(subdir, root):
1859 dirs.remove(subdir)
1860 for filename in sorted(files):
1861 # contain a pattern that matches?
1862 if ((filename_match(filename, filepatterns) and
1863 not self.excluded(filename, root))):
1864 runner(os.path.join(root, filename))
1865
1866 def excluded(self, filename, parent=None):
1867 """
1868 Check if options.exclude contains a pattern that matches filename.
1869 """
1870 if not self.options.exclude:
1871 return False
1872 basename = os.path.basename(filename)
1873 if filename_match(basename, self.options.exclude):
1874 return True
1875 if parent:
1876 filename = os.path.join(parent, filename)
1877 return filename_match(filename, self.options.exclude)
1878
1879 def ignore_code(self, code):
1880 """
1881 Check if the error code should be ignored.
1882
1883 If 'options.select' contains a prefix of the error code,
1884 return False. Else, if 'options.ignore' contains a prefix of
1885 the error code, return True.
1886 """
1887 return (code.startswith(self.options.ignore) and
1888 not code.startswith(self.options.select))
1889
1890 def get_checks(self, argument_name):
1891 """
1892 Find all globally visible functions where the first argument name
1893 starts with argument_name and which contain selected tests.
1894 """
1895 checks = []
1896 for check, attrs in _checks[argument_name].items():
1897 (codes, args) = attrs
1898 if any(not (code and self.ignore_code(code)) for code in codes):
1899 checks.append((check.__name__, check, args))
1900 return sorted(checks)
1901
1902
1903 def get_parser(prog='pep8', version=__version__):
1904 parser = OptionParser(prog=prog, version=version,
1905 usage="%prog [options] input ...")
1906 parser.config_options = [
1907 'exclude', 'filename', 'select', 'ignore', 'max-line-length',
1908 'hang-closing', 'count', 'format', 'quiet', 'show-pep8',
1909 'show-source', 'statistics', 'verbose']
1910 parser.add_option('-v', '--verbose', default=0, action='count',
1911 help="print status messages, or debug with -vv")
1912 parser.add_option('-q', '--quiet', default=0, action='count',
1913 help="report only file names, or nothing with -qq")
1914 parser.add_option('-r', '--repeat', default=True, action='store_true',
1915 help="(obsolete) show all occurrences of the same error")
1916 parser.add_option('--first', action='store_false', dest='repeat',
1917 help="show first occurrence of each error")
1918 parser.add_option('--exclude', metavar='patterns', default=DEFAULT_EXCLUDE,
1919 help="exclude files or directories which match these "
1920 "comma separated patterns (default: %default)")
1921 parser.add_option('--filename', metavar='patterns', default='*.py',
1922 help="when parsing directories, only check filenames "
1923 "matching these comma separated patterns "
1924 "(default: %default)")
1925 parser.add_option('--select', metavar='errors', default='',
1926 help="select errors and warnings (e.g. E,W6)")
1927 parser.add_option('--ignore', metavar='errors', default='',
1928 help="skip errors and warnings (e.g. E4,W)")
1929 parser.add_option('--show-source', action='store_true',
1930 help="show source code for each error")
1931 parser.add_option('--show-pep8', action='store_true',
1932 help="show text of PEP 8 for each error "
1933 "(implies --first)")
1934 parser.add_option('--statistics', action='store_true',
1935 help="count errors and warnings")
1936 parser.add_option('--count', action='store_true',
1937 help="print total number of errors and warnings "
1938 "to standard error and set exit code to 1 if "
1939 "total is not null")
1940 parser.add_option('--max-line-length', type='int', metavar='n',
1941 default=MAX_LINE_LENGTH,
1942 help="set maximum allowed line length "
1943 "(default: %default)")
1944 parser.add_option('--hang-closing', action='store_true',
1945 help="hang closing bracket instead of matching "
1946 "indentation of opening bracket's line")
1947 parser.add_option('--format', metavar='format', default='default',
1948 help="set the error format [default|pylint|<custom>]")
1949 parser.add_option('--diff', action='store_true',
1950 help="report only lines changed according to the "
1951 "unified diff received on STDIN")
1952 group = parser.add_option_group("Testing Options")
1953 if os.path.exists(TESTSUITE_PATH):
1954 group.add_option('--testsuite', metavar='dir',
1955 help="run regression tests from dir")
1956 group.add_option('--doctest', action='store_true',
1957 help="run doctest on myself")
1958 group.add_option('--benchmark', action='store_true',
1959 help="measure processing speed")
1960 return parser
1961
1962
1963 def read_config(options, args, arglist, parser):
1964 """Read both user configuration and local configuration."""
1965 config = RawConfigParser()
1966
1967 user_conf = options.config
1968 if user_conf and os.path.isfile(user_conf):
1969 if options.verbose:
1970 print('user configuration: %s' % user_conf)
1971 config.read(user_conf)
1972
1973 parent = tail = args and os.path.abspath(os.path.commonprefix(args))
1974 while tail:
1975 if config.read([os.path.join(parent, fn) for fn in PROJECT_CONFIG]):
1976 if options.verbose:
1977 print('local configuration: in %s' % parent)
1978 break
1979 parent, tail = os.path.split(parent)
1980
1981 pep8_section = parser.prog
1982 if config.has_section(pep8_section):
1983 option_list = dict([(o.dest, o.type or o.action)
1984 for o in parser.option_list])
1985
1986 # First, read the default values
1987 new_options, _ = parser.parse_args([])
1988
1989 # Second, parse the configuration
1990 for opt in config.options(pep8_section):
1991 if options.verbose > 1:
1992 print(" %s = %s" % (opt, config.get(pep8_section, opt)))
1993 if opt.replace('_', '-') not in parser.config_options:
1994 print("Unknown option: '%s'\n not in [%s]" %
1995 (opt, ' '.join(parser.config_options)))
1996 sys.exit(1)
1997 normalized_opt = opt.replace('-', '_')
1998 opt_type = option_list[normalized_opt]
1999 if opt_type in ('int', 'count'):
2000 value = config.getint(pep8_section, opt)
2001 elif opt_type == 'string':
2002 value = config.get(pep8_section, opt)
2003 else:
2004 assert opt_type in ('store_true', 'store_false')
2005 value = config.getboolean(pep8_section, opt)
2006 setattr(new_options, normalized_opt, value)
2007
2008 # Third, overwrite with the command-line options
2009 options, _ = parser.parse_args(arglist, values=new_options)
2010 options.doctest = options.testsuite = False
2011 return options
2012
2013
2014 def process_options(arglist=None, parse_argv=False, config_file=None,
2015 parser=None):
2016 """Process options passed either via arglist or via command line args."""
2017 if not arglist and not parse_argv:
2018 # Don't read the command line if the module is used as a library.
2019 arglist = []
2020 if not parser:
2021 parser = get_parser()
2022 if not parser.has_option('--config'):
2023 if config_file is True:
2024 config_file = DEFAULT_CONFIG
2025 group = parser.add_option_group("Configuration", description=(
2026 "The project options are read from the [%s] section of the "
2027 "tox.ini file or the setup.cfg file located in any parent folder "
2028 "of the path(s) being processed. Allowed options are: %s." %
2029 (parser.prog, ', '.join(parser.config_options))))
2030 group.add_option('--config', metavar='path', default=config_file,
2031 help="user config file location (default: %default)")
2032 options, args = parser.parse_args(arglist)
2033 options.reporter = None
2034
2035 if options.ensure_value('testsuite', False):
2036 args.append(options.testsuite)
2037 elif not options.ensure_value('doctest', False):
2038 if parse_argv and not args:
2039 if options.diff or any(os.path.exists(name)
2040 for name in PROJECT_CONFIG):
2041 args = ['.']
2042 else:
2043 parser.error('input not specified')
2044 options = read_config(options, args, arglist, parser)
2045 options.reporter = parse_argv and options.quiet == 1 and FileReport
2046
2047 options.filename = options.filename and options.filename.split(',')
2048 options.exclude = options.exclude.split(',')
2049 options.select = options.select and options.select.split(',')
2050 options.ignore = options.ignore and options.ignore.split(',')
2051
2052 if options.diff:
2053 options.reporter = DiffReport
2054 stdin = stdin_get_value()
2055 options.selected_lines = parse_udiff(stdin, options.filename, args[0])
2056 args = sorted(options.selected_lines)
2057
2058 return options, args
2059
2060
2061 def _main():
2062 """Parse options and run checks on Python source."""
2063 pep8style = StyleGuide(parse_argv=True, config_file=True)
2064 options = pep8style.options
2065 if options.doctest or options.testsuite:
2066 from testsuite.support import run_tests
2067 report = run_tests(pep8style)
2068 else:
2069 report = pep8style.check_files()
2070 if options.statistics:
2071 report.print_statistics()
2072 if options.benchmark:
2073 report.print_benchmark()
2074 if options.testsuite and not options.quiet:
2075 report.print_results()
2076 if report.total_errors:
2077 if options.count:
2078 sys.stderr.write(str(report.total_errors) + '\n')
2079 sys.exit(1)
2080
2081 if __name__ == '__main__':
2082 _main()

eric ide

mercurial