Merge lp:~darkmuggle-deactivatedaccount/ubuntu/oneiric/pep8/oneiric into lp:ubuntu/oneiric/pep8

Proposed by Ben Howard
Status: Superseded
Proposed branch: lp:~darkmuggle-deactivatedaccount/ubuntu/oneiric/pep8/oneiric
Merge into: lp:ubuntu/oneiric/pep8
Diff against target: 1103 lines (+460/-181)
15 files modified
CHANGES.txt (+45/-1)
PKG-INFO (+66/-7)
README.rst (+15/-4)
TODO.txt (+5/-1)
debian/changelog (+7/-0)
debian/control (+1/-1)
debian/docs (+0/-1)
debian/python-pep8.postinst.debhelper (+0/-5)
debian/python-pep8.prerm.debhelper (+0/-5)
debian/python-pep8.substvars (+0/-4)
debian/rules (+4/-0)
pep8.egg-info/PKG-INFO (+66/-7)
pep8.egg-info/SOURCES.txt (+1/-1)
pep8.py (+249/-143)
setup.py (+1/-1)
To merge this branch: bzr merge lp:~darkmuggle-deactivatedaccount/ubuntu/oneiric/pep8/oneiric
Reviewer Review Type Date Requested Status
James Page Needs Fixing
Ubuntu Sponsors Pending
Review via email: mp+70454@code.launchpad.net

Commit message

 New upstream release (LP: #817270)

Description of the change

New upstream release (LP: #817270)

To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote :

Hi Ben

I have the following feedback on your merge proposal:

lintian throws some warnings and an error against the source package:

1) debhelper files in debian directory

W: pep8 source: diff-contains-substvars debian/python-pep8.substvars
E: pep8 source: temporary-debhelper-file python-pep8.prerm.debhelper and 1 other

The debian folder contains some temp files created by debhelper; these should be removed:

python-pep8.debhelper.log
python-pep8.postinst.debhelper
python-pep8.prerm.debhelper
python-pep8.substvars

2) Standards-Version is out of date

W: pep8 source: out-of-date-standards-version 3.8.4 (current is 3.9.2)

New upstream releases are a good opportunity to bump the standards version of a package; latest is 3.9.2

See /usr/share/doc/debian-policy/upgrading-checklist.txt.gz for details of what to check but as this is a simple package I would expect minimal/no-change. This should be documented in the changelog as well.

3) dh_python2 transition

Chucks changes from version 0.5.0-1ubuntu1 have been reverted; please re-instate.

See http://wiki.debian.org/Python/TransitionToDHPython2 for more details.

..and a couple against the resulting binary package (see these by running lintian -i -I --show-overrides against the resulting .changes file)

1) Package source format

I: pep8 source: missing-debian-source-format

Ideally the package should specify the source format '3.0 (quilt)' in debian/source/format.

See http://wiki.debian.org/Projects/DebSrc3.0

2) Upstream changelog name

W: pep8: wrong-name-for-upstream-changelog usr/share/doc/pep8/CHANGES.txt

The upstream changelog should be installed to /usr/share/doc/pep8/changelog.gz; you can do this be overriding dh_installchangelogs in debian/rules:

override_dh_installchangelogs:
    dh_installchangelogs CHANGES.txt

You will need to remove it from debian/docs as well. Interestingly enough pkgbinarymangler then removes this file but this is the right way todo so that the distro chooses whether to retain it or not.

Its worth reviewing the lintian output again once these changes are made as it may reveal other warnings/errors.

You can just re-push your changes back to this branch and the merge proposal will update.

Cheers

James

review: Needs Fixing
Revision history for this message
Ben Howard (darkmuggle-deactivatedaccount) wrote :

Updated per review. Passed through litian without error.

Revision history for this message
Ben Howard (darkmuggle-deactivatedaccount) wrote :

Updated to the latest Debian upstream release for PEP8.

7. By Ben Howard

* Merge from debian unstable. (LP: #817270) Remaining Changes:
  - dh_python2 transition.
* New upstream release. (Closes: #635562)
* Remove depends on python-setuptools. (Closes: #581019)
* Update Standards version, no changes needed.

Unmerged revisions

7. By Ben Howard

* Merge from debian unstable. (LP: #817270) Remaining Changes:
  - dh_python2 transition.
* New upstream release. (Closes: #635562)
* Remove depends on python-setuptools. (Closes: #581019)
* Update Standards version, no changes needed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CHANGES.txt'
2--- CHANGES.txt 2010-03-16 19:18:00 +0000
3+++ CHANGES.txt 2011-08-05 17:38:30 +0000
4@@ -1,6 +1,50 @@
5 Changelog
6 =========
7
8+0.6.1 (2010-10-03)
9+------------------
10+
11+* Fixed inconsistent version numbers. (Issue #21)
12+
13+
14+0.6.0 (2010-09-19)
15+------------------
16+
17+* Test suite reorganized and enhanced in order to check more failures
18+ with fewer test files. Read the ``run_tests`` docstring for details
19+ about the syntax.
20+
21+* Fix E225: accept ``print >>sys.stderr, "..."`` syntax.
22+
23+* Fix E501 for lines containing multibyte encoded characters. (Issue #7)
24+
25+* Fix E221, E222, E223, E224 not detected in some cases. (Issue #16)
26+
27+* Fix E211 to reject ``v = dic['a'] ['b']``. (Issue #17)
28+
29+* Exit code is always 1 if any error or warning is found. (Issue #10)
30+
31+* ``--ignore`` checks are now really ignored, especially in
32+ conjunction with ``--count``. (Issue #8)
33+
34+* Blank lines with spaces yield W293 instead of W291: some developers
35+ want to ignore this warning and indent the blank lines to paste their
36+ code easily in the Python interpreter.
37+
38+* Fix E301: do not require a blank line before an indented block. (Issue #14)
39+
40+* Fix E203 to accept NumPy slice notation ``a[0, :]``. (Issue #13)
41+
42+* Performance improvements.
43+
44+* Fix decoding and checking non-UTF8 files in Python 3.
45+
46+* Fix E225: reject ``True+False`` when running on Python 3.
47+
48+* Fix an exception when the line starts with an operator.
49+
50+* Allow a new line before closing ``)``, ``}`` or ``]``. (Issue #5)
51+
52
53 0.5.0 (2010-02-17)
54 ------------------
55@@ -63,7 +107,7 @@
56
57 * E231 should allow commas to be followed by ``)``. (Issue #3 [1]_)
58
59-* Spaces are not longer required around the equals sign for keyword
60+* Spaces are no longer required around the equals sign for keyword
61 arguments or default parameter values.
62
63
64
65=== modified file 'PKG-INFO'
66--- PKG-INFO 2010-03-16 19:18:00 +0000
67+++ PKG-INFO 2011-08-05 17:38:30 +0000
68@@ -1,6 +1,6 @@
69 Metadata-Version: 1.0
70 Name: pep8
71-Version: 0.5.0
72+Version: 0.6.1
73 Summary: Python style guide checker
74 Home-page: http://github.com/jcrocholl/pep8
75 Author: Johann C. Rocholl
76@@ -30,14 +30,25 @@
77 * Small: Just one Python file, requires only stdlib. You can use just
78 the pep8.py file for this purpose.
79
80- * Easy_installable, of course!
81-
82+ * Comes with a comprehensive test suite.
83
84 Installation
85 ------------
86
87- Just an ``easy_install pep8`` ought to do the trick.
88-
89+ You can install, upgrade, uninstall pep8.py with these commands::
90+
91+ $ sudo pip install pep8
92+ $ sudo pip install --upgrade pep8
93+ $ sudo pip uninstall pep8
94+
95+ Or if you don't have `pip`::
96+
97+ $ sudo easy_install pep8
98+
99+ There's also a package for Debian/Ubuntu, but it's not always the
100+ latest version::
101+
102+ $ sudo apt-get install pep8
103
104 Example usage and output
105 ------------------------
106@@ -127,6 +138,50 @@
107 Changelog
108 =========
109
110+ 0.6.1 (2010-10-03)
111+ ------------------
112+
113+ * Fixed inconsistent version numbers. (Issue #21)
114+
115+
116+ 0.6.0 (2010-09-19)
117+ ------------------
118+
119+ * Test suite reorganized and enhanced in order to check more failures
120+ with fewer test files. Read the ``run_tests`` docstring for details
121+ about the syntax.
122+
123+ * Fix E225: accept ``print >>sys.stderr, "..."`` syntax.
124+
125+ * Fix E501 for lines containing multibyte encoded characters. (Issue #7)
126+
127+ * Fix E221, E222, E223, E224 not detected in some cases. (Issue #16)
128+
129+ * Fix E211 to reject ``v = dic['a'] ['b']``. (Issue #17)
130+
131+ * Exit code is always 1 if any error or warning is found. (Issue #10)
132+
133+ * ``--ignore`` checks are now really ignored, especially in
134+ conjunction with ``--count``. (Issue #8)
135+
136+ * Blank lines with spaces yield W293 instead of W291: some developers
137+ want to ignore this warning and indent the blank lines to paste their
138+ code easily in the Python interpreter.
139+
140+ * Fix E301: do not require a blank line before an indented block. (Issue #14)
141+
142+ * Fix E203 to accept NumPy slice notation ``a[0, :]``. (Issue #13)
143+
144+ * Performance improvements.
145+
146+ * Fix decoding and checking non-UTF8 files in Python 3.
147+
148+ * Fix E225: reject ``True+False`` when running on Python 3.
149+
150+ * Fix an exception when the line starts with an operator.
151+
152+ * Allow a new line before closing ``)``, ``}`` or ``]``. (Issue #5)
153+
154
155 0.5.0 (2010-02-17)
156 ------------------
157@@ -189,7 +244,7 @@
158
159 * E231 should allow commas to be followed by ``)``. (Issue #3 [1]_)
160
161- * Spaces are not longer required around the equals sign for keyword
162+ * Spaces are no longer required around the equals sign for keyword
163 arguments or default parameter values.
164
165
166@@ -221,7 +276,11 @@
167 TODO
168 ====
169
170- - ...
171+ - Should command line option --repeat be enabled by default?
172+
173+ - Does command line option --ignore properly prevent status code 1?
174+
175+ - Release version 1.0 after a brief stabilization period.
176
177 Keywords: pep8
178 Platform: UNKNOWN
179
180=== modified file 'README.rst'
181--- README.rst 2010-03-16 19:18:00 +0000
182+++ README.rst 2011-08-05 17:38:30 +0000
183@@ -22,14 +22,25 @@
184 * Small: Just one Python file, requires only stdlib. You can use just
185 the pep8.py file for this purpose.
186
187-* Easy_installable, of course!
188-
189+* Comes with a comprehensive test suite.
190
191 Installation
192 ------------
193
194-Just an ``easy_install pep8`` ought to do the trick.
195-
196+You can install, upgrade, uninstall pep8.py with these commands::
197+
198+ $ sudo pip install pep8
199+ $ sudo pip install --upgrade pep8
200+ $ sudo pip uninstall pep8
201+
202+Or if you don't have `pip`::
203+
204+ $ sudo easy_install pep8
205+
206+There's also a package for Debian/Ubuntu, but it's not always the
207+latest version::
208+
209+ $ sudo apt-get install pep8
210
211 Example usage and output
212 ------------------------
213
214=== modified file 'TODO.txt'
215--- TODO.txt 2010-03-16 19:18:00 +0000
216+++ TODO.txt 2011-08-05 17:38:30 +0000
217@@ -1,4 +1,8 @@
218 TODO
219 ====
220
221-- ...
222+- Should command line option --repeat be enabled by default?
223+
224+- Does command line option --ignore properly prevent status code 1?
225+
226+- Release version 1.0 after a brief stabilization period.
227
228=== modified file 'debian/changelog'
229--- debian/changelog 2011-07-07 09:09:25 +0000
230+++ debian/changelog 2011-08-05 17:38:30 +0000
231@@ -1,3 +1,10 @@
232+pep8 (0.6.1-0ubuntu1) oneiric; urgency=low
233+
234+ * New upstream release (LP: #817270)
235+ * Switch to dpkg-source 3.0 (quilt) format
236+
237+ -- Ben Howard <ben.howard@canonical.com> Fri, 05 Aug 2011 08:45:10 -0600
238+
239 pep8 (0.5.0-1ubuntu1) oneiric; urgency=low
240
241 * dh_python2 transition.
242
243=== modified file 'debian/control'
244--- debian/control 2011-07-07 09:09:25 +0000
245+++ debian/control 2011-08-05 17:38:30 +0000
246@@ -5,7 +5,7 @@
247 XSBC-Original-Maintainer: David Watson <david@bashton.com>
248 Uploaders: David Watson <dwatson@debian.org>
249 Build-Depends: debhelper (>= 7), python-all (>= 2.6.6-3~), python-setuptools
250-Standards-Version: 3.8.4
251+Standards-Version: 3.9.2
252 Homepage: http://pypi.python.org/pypi/pep8
253
254 Package: pep8
255
256=== modified file 'debian/docs'
257--- debian/docs 2009-10-23 10:11:07 +0000
258+++ debian/docs 2011-08-05 17:38:30 +0000
259@@ -1,3 +1,2 @@
260-CHANGES.txt
261 README.rst
262 TODO.txt
263
264=== removed file 'debian/python-pep8.postinst.debhelper'
265--- debian/python-pep8.postinst.debhelper 2009-10-23 10:11:07 +0000
266+++ debian/python-pep8.postinst.debhelper 1970-01-01 00:00:00 +0000
267@@ -1,5 +0,0 @@
268-# Automatically added by dh_pysupport
269-if which update-python-modules >/dev/null 2>&1; then
270- update-python-modules python-pep8.public
271-fi
272-# End automatically added section
273
274=== removed file 'debian/python-pep8.prerm.debhelper'
275--- debian/python-pep8.prerm.debhelper 2009-10-23 10:11:07 +0000
276+++ debian/python-pep8.prerm.debhelper 1970-01-01 00:00:00 +0000
277@@ -1,5 +0,0 @@
278-# Automatically added by dh_pysupport
279-if which update-python-modules >/dev/null 2>&1; then
280- update-python-modules -c python-pep8.public
281-fi
282-# End automatically added section
283
284=== removed file 'debian/python-pep8.substvars'
285--- debian/python-pep8.substvars 2009-10-23 10:11:07 +0000
286+++ debian/python-pep8.substvars 1970-01-01 00:00:00 +0000
287@@ -1,4 +0,0 @@
288-python:Versions=2.4, 2.5
289-python:Provides=python2.4-pep8, python2.5-pep8
290-python:Depends=python, python-support (>= 0.90.0)
291-misc:Depends=
292
293=== modified file 'debian/rules'
294--- debian/rules 2011-07-07 09:09:25 +0000
295+++ debian/rules 2011-08-05 17:38:30 +0000
296@@ -11,3 +11,7 @@
297
298 %:
299 dh $@ --with python2
300+
301+override_dh_installchangelogs:
302+ dh_installchangelogs CHANGES.txt
303+
304
305=== modified file 'pep8.egg-info/PKG-INFO'
306--- pep8.egg-info/PKG-INFO 2010-03-16 19:18:00 +0000
307+++ pep8.egg-info/PKG-INFO 2011-08-05 17:38:30 +0000
308@@ -1,6 +1,6 @@
309 Metadata-Version: 1.0
310 Name: pep8
311-Version: 0.5.0
312+Version: 0.6.1
313 Summary: Python style guide checker
314 Home-page: http://github.com/jcrocholl/pep8
315 Author: Johann C. Rocholl
316@@ -30,14 +30,25 @@
317 * Small: Just one Python file, requires only stdlib. You can use just
318 the pep8.py file for this purpose.
319
320- * Easy_installable, of course!
321-
322+ * Comes with a comprehensive test suite.
323
324 Installation
325 ------------
326
327- Just an ``easy_install pep8`` ought to do the trick.
328-
329+ You can install, upgrade, uninstall pep8.py with these commands::
330+
331+ $ sudo pip install pep8
332+ $ sudo pip install --upgrade pep8
333+ $ sudo pip uninstall pep8
334+
335+ Or if you don't have `pip`::
336+
337+ $ sudo easy_install pep8
338+
339+ There's also a package for Debian/Ubuntu, but it's not always the
340+ latest version::
341+
342+ $ sudo apt-get install pep8
343
344 Example usage and output
345 ------------------------
346@@ -127,6 +138,50 @@
347 Changelog
348 =========
349
350+ 0.6.1 (2010-10-03)
351+ ------------------
352+
353+ * Fixed inconsistent version numbers. (Issue #21)
354+
355+
356+ 0.6.0 (2010-09-19)
357+ ------------------
358+
359+ * Test suite reorganized and enhanced in order to check more failures
360+ with fewer test files. Read the ``run_tests`` docstring for details
361+ about the syntax.
362+
363+ * Fix E225: accept ``print >>sys.stderr, "..."`` syntax.
364+
365+ * Fix E501 for lines containing multibyte encoded characters. (Issue #7)
366+
367+ * Fix E221, E222, E223, E224 not detected in some cases. (Issue #16)
368+
369+ * Fix E211 to reject ``v = dic['a'] ['b']``. (Issue #17)
370+
371+ * Exit code is always 1 if any error or warning is found. (Issue #10)
372+
373+ * ``--ignore`` checks are now really ignored, especially in
374+ conjunction with ``--count``. (Issue #8)
375+
376+ * Blank lines with spaces yield W293 instead of W291: some developers
377+ want to ignore this warning and indent the blank lines to paste their
378+ code easily in the Python interpreter.
379+
380+ * Fix E301: do not require a blank line before an indented block. (Issue #14)
381+
382+ * Fix E203 to accept NumPy slice notation ``a[0, :]``. (Issue #13)
383+
384+ * Performance improvements.
385+
386+ * Fix decoding and checking non-UTF8 files in Python 3.
387+
388+ * Fix E225: reject ``True+False`` when running on Python 3.
389+
390+ * Fix an exception when the line starts with an operator.
391+
392+ * Allow a new line before closing ``)``, ``}`` or ``]``. (Issue #5)
393+
394
395 0.5.0 (2010-02-17)
396 ------------------
397@@ -189,7 +244,7 @@
398
399 * E231 should allow commas to be followed by ``)``. (Issue #3 [1]_)
400
401- * Spaces are not longer required around the equals sign for keyword
402+ * Spaces are no longer required around the equals sign for keyword
403 arguments or default parameter values.
404
405
406@@ -221,7 +276,11 @@
407 TODO
408 ====
409
410- - ...
411+ - Should command line option --repeat be enabled by default?
412+
413+ - Does command line option --ignore properly prevent status code 1?
414+
415+ - Release version 1.0 after a brief stabilization period.
416
417 Keywords: pep8
418 Platform: UNKNOWN
419
420=== modified file 'pep8.egg-info/SOURCES.txt'
421--- pep8.egg-info/SOURCES.txt 2010-03-16 19:18:00 +0000
422+++ pep8.egg-info/SOURCES.txt 2011-08-05 17:38:30 +0000
423@@ -12,4 +12,4 @@
424 pep8.egg-info/namespace_packages.txt
425 pep8.egg-info/not-zip-safe
426 pep8.egg-info/requires.txt
427-pep8.egg-info/top_level.txt
428+pep8.egg-info/top_level.txt
429\ No newline at end of file
430
431=== modified file 'pep8.py'
432--- pep8.py 2010-03-16 19:18:00 +0000
433+++ pep8.py 2011-08-05 17:38:30 +0000
434@@ -92,34 +92,51 @@
435
436 """
437
438-__version__ = '0.5.0'
439+__version__ = '0.6.1'
440
441 import os
442 import sys
443 import re
444 import time
445 import inspect
446+import keyword
447 import tokenize
448 from optparse import OptionParser
449-from keyword import iskeyword
450 from fnmatch import fnmatch
451+try:
452+ frozenset
453+except NameError:
454+ from sets import ImmutableSet as frozenset
455+
456
457 DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git'
458-DEFAULT_IGNORE = ['E24']
459+DEFAULT_IGNORE = 'E24'
460+MAX_LINE_LENGTH = 79
461
462 INDENT_REGEX = re.compile(r'([ \t]*)')
463 RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)')
464 SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)')
465 ERRORCODE_REGEX = re.compile(r'[EW]\d{3}')
466-E301NOT_REGEX = re.compile(r'class |def |u?r?["\']')
467+DOCSTRING_REGEX = re.compile(r'u?r?["\']')
468+WHITESPACE_AROUND_OPERATOR_REGEX = \
469+ re.compile('([^\w\s]*)\s*(\t| )\s*([^\w\s]*)')
470+EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
471+WHITESPACE_AROUND_NAMED_PARAMETER_REGEX = \
472+ re.compile(r'[()]|\s=[^=]|[^=!<>]=\s')
473+
474
475 WHITESPACE = ' \t'
476
477-BINARY_OPERATORS = ['**=', '*=', '+=', '-=', '!=', '<>',
478- '%=', '^=', '&=', '|=', '==', '/=', '//=', '>=', '<=', '>>=', '<<=',
479- '%', '^', '&', '|', '=', '/', '//', '>', '<', '>>', '<<']
480-UNARY_OPERATORS = ['**', '*', '+', '-']
481-OPERATORS = BINARY_OPERATORS + UNARY_OPERATORS
482+BINARY_OPERATORS = frozenset(['**=', '*=', '+=', '-=', '!=', '<>',
483+ '%=', '^=', '&=', '|=', '==', '/=', '//=', '<=', '>=', '<<=', '>>=',
484+ '%', '^', '&', '|', '=', '/', '//', '<', '>', '<<'])
485+UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-'])
486+OPERATORS = BINARY_OPERATORS | UNARY_OPERATORS
487+SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.INDENT,
488+ tokenize.DEDENT, tokenize.NEWLINE])
489+E225NOT_KEYWORDS = (frozenset(keyword.kwlist + ['print']) -
490+ frozenset(['False', 'None', 'True']))
491+BENCHMARK_KEYS = ('directories', 'files', 'logical lines', 'physical lines')
492
493 options = None
494 args = None
495@@ -164,18 +181,33 @@
496
497
498 def trailing_whitespace(physical_line):
499- """
500+ r"""
501 JCR: Trailing whitespace is superfluous.
502+ FBM: Except when it occurs as part of a blank line (i.e. the line is
503+ nothing but whitespace). According to Python docs[1] a line with only
504+ whitespace is considered a blank line, and is to be ignored. However,
505+ matching a blank line to its indentation level avoids mistakenly
506+ terminating a multi-line statement (e.g. class declaration) when
507+ pasting code into the standard Python interpreter.
508+
509+ [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines
510+
511+ The warning returned varies on whether the line itself is blank, for easier
512+ filtering for those who want to indent their blank lines.
513
514 Okay: spam(1)
515 W291: spam(1)\s
516+ W293: class Foo(object):\n \n bang = 12
517 """
518 physical_line = physical_line.rstrip('\n') # chr(10), newline
519 physical_line = physical_line.rstrip('\r') # chr(13), carriage return
520 physical_line = physical_line.rstrip('\x0c') # chr(12), form feed, ^L
521 stripped = physical_line.rstrip()
522 if physical_line != stripped:
523- return len(stripped), "W291 trailing whitespace"
524+ if stripped:
525+ return len(stripped), "W291 trailing whitespace"
526+ else:
527+ return 0, "W293 blank line contains whitespace"
528
529
530 def trailing_blank_lines(physical_line, lines, line_number):
531@@ -208,9 +240,18 @@
532 For flowing long blocks of text (docstrings or comments), limiting the
533 length to 72 characters is recommended.
534 """
535- length = len(physical_line.rstrip())
536- if length > 79:
537- return 79, "E501 line too long (%d characters)" % length
538+ line = physical_line.rstrip()
539+ length = len(line)
540+ if length > MAX_LINE_LENGTH:
541+ try:
542+ # The line could contain multi-byte characters
543+ if not hasattr(line, 'decode'): # Python 3
544+ line = line.encode('latin-1')
545+ length = len(line.decode('utf-8'))
546+ except UnicodeDecodeError:
547+ pass
548+ if length > MAX_LINE_LENGTH:
549+ return MAX_LINE_LENGTH, "E501 line too long (%d characters)" % length
550
551
552 ##############################################################################
553@@ -219,7 +260,8 @@
554
555
556 def blank_lines(logical_line, blank_lines, indent_level, line_number,
557- previous_logical, blank_lines_before_comment):
558+ previous_logical, previous_indent_level,
559+ blank_lines_before_comment):
560 r"""
561 Separate top-level function and class definitions with two blank lines.
562
563@@ -252,7 +294,8 @@
564 logical_line.startswith('class ') or
565 logical_line.startswith('@')):
566 if indent_level:
567- if not (max_blank_lines or E301NOT_REGEX.match(previous_logical)):
568+ if not (max_blank_lines or previous_indent_level < indent_level or
569+ DOCSTRING_REGEX.match(previous_logical)):
570 return 0, "E301 expected 1 blank line, found 0"
571 elif max_blank_lines != 2:
572 return 0, "E302 expected 2 blank lines, found %d" % max_blank_lines
573@@ -279,18 +322,17 @@
574 E203: if x == 4 : print x, y; x, y = y, x
575 """
576 line = logical_line
577- for char in '([{':
578- found = line.find(char + ' ')
579- if found > -1:
580+ for match in EXTRANEOUS_WHITESPACE_REGEX.finditer(line):
581+ text = match.group()
582+ char = text.strip()
583+ found = match.start()
584+ if text == char + ' ' and char in '([{':
585 return found + 1, "E201 whitespace after '%s'" % char
586- for char in '}])':
587- found = line.find(' ' + char)
588- if found > -1 and line[found - 1] != ',':
589- return found, "E202 whitespace before '%s'" % char
590- for char in ',;:':
591- found = line.find(' ' + char)
592- if found > -1:
593- return found, "E203 whitespace before '%s'" % char
594+ if text == ' ' + char and line[found - 1] != ',':
595+ if char in '}])':
596+ return found, "E202 whitespace before '%s'" % char
597+ if char in ',;:':
598+ return found, "E203 whitespace before '%s'" % char
599
600
601 def missing_whitespace(logical_line):
602@@ -370,9 +412,11 @@
603 if (token_type == tokenize.OP and
604 text in '([' and
605 start != prev_end and
606- prev_type == tokenize.NAME and
607+ (prev_type == tokenize.NAME or prev_text in '}])') and
608+ # Syntax "class A (B):" is allowed, but avoid it
609 (index < 2 or tokens[index - 2][1] != 'class') and
610- (not iskeyword(prev_text))):
611+ # Allow "return (a.foo for a in range(5))"
612+ (not keyword.iskeyword(prev_text))):
613 return prev_end, "E211 whitespace before '%s'" % text
614 prev_type = token_type
615 prev_text = text
616@@ -392,20 +436,16 @@
617 E223: a = 4\t+ 5
618 E224: a = 4 +\t5
619 """
620- line = logical_line
621- for operator in OPERATORS:
622- found = line.find(' ' + operator)
623- if found > -1:
624- return found, "E221 multiple spaces before operator"
625- found = line.find(operator + ' ')
626- if found > -1:
627- return found, "E222 multiple spaces after operator"
628- found = line.find('\t' + operator)
629- if found > -1:
630- return found, "E223 tab before operator"
631- found = line.find(operator + '\t')
632- if found > -1:
633- return found, "E224 tab after operator"
634+ for match in WHITESPACE_AROUND_OPERATOR_REGEX.finditer(logical_line):
635+ before, whitespace, after = match.groups()
636+ tab = whitespace == '\t'
637+ offset = match.start(2)
638+ if before in OPERATORS:
639+ return offset, (tab and "E224 tab after operator" or
640+ "E222 multiple spaces after operator")
641+ elif after in OPERATORS:
642+ return offset, (tab and "E223 tab before operator" or
643+ "E221 multiple spaces before operator")
644
645
646 def missing_whitespace_around_operator(logical_line, tokens):
647@@ -451,20 +491,29 @@
648 elif text == ')':
649 parens -= 1
650 if need_space:
651- if start == prev_end:
652+ if start != prev_end:
653+ need_space = False
654+ elif text == '>' and prev_text == '<':
655+ # Tolerate the "<>" operator, even if running Python 3
656+ pass
657+ else:
658 return prev_end, "E225 missing whitespace around operator"
659- need_space = False
660- elif token_type == tokenize.OP:
661+ elif token_type == tokenize.OP and prev_end is not None:
662 if text == '=' and parens:
663 # Allow keyword args or defaults: foo(bar=None).
664 pass
665 elif text in BINARY_OPERATORS:
666 need_space = True
667 elif text in UNARY_OPERATORS:
668- if ((prev_type != tokenize.OP or prev_text in '}])') and not
669- (prev_type == tokenize.NAME and iskeyword(prev_text))):
670- # Allow unary operators: -123, -x, +1.
671- # Allow argument unpacking: foo(*args, **kwargs).
672+ # Allow unary operators: -123, -x, +1.
673+ # Allow argument unpacking: foo(*args, **kwargs).
674+ if prev_type == tokenize.OP:
675+ if prev_text in '}])':
676+ need_space = True
677+ elif prev_type == tokenize.NAME:
678+ if prev_text not in E225NOT_KEYWORDS:
679+ need_space = True
680+ else:
681 need_space = True
682 if need_space and start == prev_end:
683 return prev_end, "E225 missing whitespace around operator"
684@@ -513,23 +562,15 @@
685 E251: return magic(r = real, i = imag)
686 """
687 parens = 0
688- window = ' '
689- equal_ok = ['==', '!=', '<=', '>=']
690-
691- for pos, c in enumerate(logical_line):
692- window = window[1:] + c
693- if parens:
694- if window[0] in WHITESPACE and window[1] == '=':
695- if window[1:] not in equal_ok:
696- issue = "E251 no spaces around keyword / parameter equals"
697- return pos, issue
698- if window[2] in WHITESPACE and window[1] == '=':
699- if window[:2] not in equal_ok:
700- issue = "E251 no spaces around keyword / parameter equals"
701- return pos, issue
702- if c == '(':
703+ for match in WHITESPACE_AROUND_NAMED_PARAMETER_REGEX.finditer(
704+ logical_line):
705+ text = match.group()
706+ if parens and len(text) == 3:
707+ issue = "E251 no spaces around keyword / parameter equals"
708+ return match.start(), issue
709+ if text == '(':
710 parens += 1
711- elif c == ')':
712+ elif text == ')':
713 parens -= 1
714
715
716@@ -676,6 +717,18 @@
717 ##############################################################################
718
719
720+if '' == ''.encode():
721+ # Python 2: implicit encoding.
722+ def readlines(filename):
723+ return open(filename).readlines()
724+else:
725+ # Python 3: decode to latin-1.
726+ # This function is lazy, it does not read the encoding declaration.
727+ # XXX: use tokenize.detect_encoding()
728+ def readlines(filename):
729+ return open(filename, encoding='latin-1').readlines()
730+
731+
732 def expand_indent(line):
733 """
734 Return the amount of indentation.
735@@ -765,19 +818,16 @@
736 Load a Python source file, tokenize it, check coding style.
737 """
738
739- def __init__(self, filename):
740- if filename:
741- self.filename = filename
742- try:
743- self.lines = open(filename).readlines()
744- except UnicodeDecodeError:
745- # Errors may occur with non-UTF8 files in Python 3000
746- self.lines = open(filename, errors='replace').readlines()
747- else:
748+ def __init__(self, filename, lines=None):
749+ self.filename = filename
750+ if filename is None:
751 self.filename = 'stdin'
752- self.lines = []
753- options.counters['physical lines'] = \
754- options.counters.get('physical lines', 0) + len(self.lines)
755+ self.lines = lines or []
756+ elif lines is None:
757+ self.lines = readlines(filename)
758+ else:
759+ self.lines = lines
760+ options.counters['physical lines'] += len(self.lines)
761
762 def readline(self):
763 """
764@@ -830,9 +880,7 @@
765 previous = None
766 for token in self.tokens:
767 token_type, text = token[0:2]
768- if token_type in (tokenize.COMMENT, tokenize.NL,
769- tokenize.INDENT, tokenize.DEDENT,
770- tokenize.NEWLINE):
771+ if token_type in SKIP_TOKENS:
772 continue
773 if token_type == tokenize.STRING:
774 text = mute_string(text)
775@@ -840,7 +888,9 @@
776 end_line, end = previous[3]
777 start_line, start = token[2]
778 if end_line != start_line: # different row
779- if self.lines[end_line - 1][end - 1] not in '{[(':
780+ prev_text = self.lines[end_line - 1][end - 1]
781+ if prev_text == ',' or (prev_text not in '{[('
782+ and text not in '}])'):
783 logical.append(' ')
784 length += 1
785 elif end != start: # different column
786@@ -859,8 +909,7 @@
787 """
788 Build a line from tokens and run all logical checks on it.
789 """
790- options.counters['logical lines'] = \
791- options.counters.get('logical lines', 0) + 1
792+ options.counters['logical lines'] += 1
793 self.build_tokens_line()
794 first_line = self.lines[self.mapping[0][1][2][0] - 1]
795 indent = first_line[:self.mapping[0][1][2][1]]
796@@ -869,8 +918,8 @@
797 if options.verbose >= 2:
798 print(self.logical_line[:80].rstrip())
799 for name, check, argument_names in options.logical_checks:
800- if options.verbose >= 3:
801- print(' ', name)
802+ if options.verbose >= 4:
803+ print(' ' + name)
804 result = self.run_check(check, argument_names)
805 if result is not None:
806 offset, text = result
807@@ -886,12 +935,14 @@
808 text, check)
809 self.previous_logical = self.logical_line
810
811- def check_all(self):
812+ def check_all(self, expected=None, line_offset=0):
813 """
814 Run all checks on the input file.
815 """
816+ self.expected = expected or ()
817+ self.line_offset = line_offset
818+ self.line_number = 0
819 self.file_errors = 0
820- self.line_number = 0
821 self.indent_char = None
822 self.indent_level = 0
823 self.previous_logical = ''
824@@ -900,7 +951,13 @@
825 self.tokens = []
826 parens = 0
827 for token in tokenize.generate_tokens(self.readline_check_physical):
828- # print(tokenize.tok_name[token[0]], repr(token))
829+ if options.verbose >= 3:
830+ if token[2][0] == token[3][0]:
831+ pos = '[%s:%s]' % (token[2][1] or '', token[3][1])
832+ else:
833+ pos = 'l.%s' % token[3][0]
834+ print('l.%s\t%s\t%s\t%r' %
835+ (token[2][0], pos, tokenize.tok_name[token[0]], token[1]))
836 self.tokens.append(token)
837 token_type, text = token[0:2]
838 if token_type == tokenize.OP and text in '([{':
839@@ -935,25 +992,24 @@
840 """
841 Report an error, according to options.
842 """
843+ code = text[:4]
844+ if ignore_code(code):
845+ return
846 if options.quiet == 1 and not self.file_errors:
847 message(self.filename)
848+ if code in options.counters:
849+ options.counters[code] += 1
850+ else:
851+ options.counters[code] = 1
852+ options.messages[code] = text[5:]
853+ if options.quiet or code in self.expected:
854+ # Don't care about expected errors or warnings
855+ return
856 self.file_errors += 1
857- code = text[:4]
858- options.counters[code] = options.counters.get(code, 0) + 1
859- options.messages[code] = text[5:]
860- if options.quiet:
861- return
862- if options.testsuite:
863- basename = os.path.basename(self.filename)
864- if basename[:4] != code:
865- return # Don't care about other errors or warnings
866- if 'not' not in basename:
867- return # Don't print the expected error message
868- if ignore_code(code):
869- return
870 if options.counters[code] == 1 or options.repeat:
871 message("%s:%s:%d: %s" %
872- (self.filename, line_number, offset + 1, text))
873+ (self.filename, self.line_offset + line_number,
874+ offset + 1, text))
875 if options.show_source:
876 line = self.lines[line_number - 1]
877 message(line.rstrip())
878@@ -966,43 +1022,33 @@
879 """
880 Run all checks on a Python source file.
881 """
882- if excluded(filename):
883- return {}
884 if options.verbose:
885 message('checking ' + filename)
886- files_counter_before = options.counters.get('files', 0)
887- if options.testsuite: # Keep showing errors for multiple tests
888- options.counters = {}
889- options.counters['files'] = files_counter_before + 1
890 errors = Checker(filename).check_all()
891- if options.testsuite: # Check if the expected error was found
892- basename = os.path.basename(filename)
893- code = basename[:4]
894- count = options.counters.get(code, 0)
895- if count == 0 and 'not' not in basename:
896- message("%s: error %s not found" % (filename, code))
897-
898-
899-def input_dir(dirname):
900+
901+
902+def input_dir(dirname, runner=None):
903 """
904 Check all Python source files in this directory and all subdirectories.
905 """
906 dirname = dirname.rstrip('/')
907 if excluded(dirname):
908 return
909+ if runner is None:
910+ runner = input_file
911 for root, dirs, files in os.walk(dirname):
912 if options.verbose:
913 message('directory ' + root)
914- options.counters['directories'] = \
915- options.counters.get('directories', 0) + 1
916+ options.counters['directories'] += 1
917 dirs.sort()
918 for subdir in dirs:
919 if excluded(subdir):
920 dirs.remove(subdir)
921 files.sort()
922 for filename in files:
923- if filename_match(filename):
924- input_file(os.path.join(root, filename))
925+ if filename_match(filename) and not excluded(filename):
926+ options.counters['files'] += 1
927+ runner(os.path.join(root, filename))
928
929
930 def excluded(filename):
931@@ -1041,6 +1087,13 @@
932 return True
933
934
935+def reset_counters():
936+ for key in list(options.counters.keys()):
937+ if key not in BENCHMARK_KEYS:
938+ del options.counters[key]
939+ options.messages = {}
940+
941+
942 def get_error_statistics():
943 """Get error statistics."""
944 return get_statistics("E")
945@@ -1091,13 +1144,60 @@
946 Print benchmark numbers.
947 """
948 print('%-7.2f %s' % (elapsed, 'seconds elapsed'))
949- keys = ['directories', 'files',
950- 'logical lines', 'physical lines']
951- for key in keys:
952- if key in options.counters:
953- print('%-7d %s per second (%d total)' % (
954- options.counters[key] / elapsed, key,
955- options.counters[key]))
956+ for key in BENCHMARK_KEYS:
957+ print('%-7d %s per second (%d total)' % (
958+ options.counters[key] / elapsed, key,
959+ options.counters[key]))
960+
961+
962+def run_tests(filename):
963+ """
964+ Run all the tests from a file.
965+
966+ A test file can provide many tests. Each test starts with a declaration.
967+ This declaration is a single line starting with '#:'.
968+ It declares codes of expected failures, separated by spaces or 'Okay'
969+ if no failure is expected.
970+ If the file does not contain such declaration, it should pass all tests.
971+ If the declaration is empty, following lines are not checked, until next
972+ declaration.
973+
974+ Examples:
975+
976+ * Only E224 and W701 are expected: #: E224 W701
977+ * Following example is conform: #: Okay
978+ * Don't check these lines: #:
979+ """
980+ lines = readlines(filename) + ['#:\n']
981+ line_offset = 0
982+ codes = ['Okay']
983+ testcase = []
984+ for index, line in enumerate(lines):
985+ if not line.startswith('#:'):
986+ if codes:
987+ # Collect the lines of the test case
988+ testcase.append(line)
989+ continue
990+ if codes and index > 0:
991+ label = '%s:%s:1' % (filename, line_offset + 1)
992+ codes = [c for c in codes if c != 'Okay']
993+ # Run the checker
994+ errors = Checker(filename, testcase).check_all(codes, line_offset)
995+ # Check if the expected errors were found
996+ for code in codes:
997+ if not options.counters.get(code):
998+ errors += 1
999+ message('%s: error %s not found' % (label, code))
1000+ if options.verbose and not errors:
1001+ message('%s: passed (%s)' % (label, ' '.join(codes)))
1002+ # Keep showing errors for multiple tests
1003+ reset_counters()
1004+ # output the real line numbers
1005+ line_offset = index
1006+ # configure the expected errors
1007+ codes = line.split()[1:]
1008+ # empty the test case buffer
1009+ del testcase[:]
1010
1011
1012 def selftest():
1013@@ -1120,16 +1220,17 @@
1014 part = part.replace(r'\s', ' ')
1015 checker.lines.append(part + '\n')
1016 options.quiet = 2
1017- options.counters = {}
1018 checker.check_all()
1019 error = None
1020 if code == 'Okay':
1021- if len(options.counters) > 1:
1022+ if len(options.counters) > len(BENCHMARK_KEYS):
1023 codes = [key for key in options.counters.keys()
1024- if key != 'logical lines']
1025+ if key not in BENCHMARK_KEYS]
1026 error = "incorrectly found %s" % ', '.join(codes)
1027- elif options.counters.get(code, 0) == 0:
1028+ elif not options.counters.get(code):
1029 error = "failed to find %s" % code
1030+ # Reset the counters
1031+ reset_counters()
1032 if not error:
1033 count_passed += 1
1034 else:
1035@@ -1193,7 +1294,7 @@
1036 options, args = parser.parse_args(arglist)
1037 if options.testsuite:
1038 args.append(options.testsuite)
1039- if len(args) == 0 and not options.doctest:
1040+ if not args and not options.doctest:
1041 parser.error('input not specified')
1042 options.prog = os.path.basename(sys.argv[0])
1043 options.exclude = options.exclude.split(',')
1044@@ -1215,10 +1316,10 @@
1045 options.ignore = []
1046 else:
1047 # The default choice: ignore controversial checks
1048- options.ignore = DEFAULT_IGNORE
1049+ options.ignore = DEFAULT_IGNORE.split(',')
1050 options.physical_checks = find_checks('physical_line')
1051 options.logical_checks = find_checks('logical_line')
1052- options.counters = {}
1053+ options.counters = dict.fromkeys(BENCHMARK_KEYS, 0)
1054 options.messages = {}
1055 return options, args
1056
1057@@ -1232,22 +1333,27 @@
1058 import doctest
1059 doctest.testmod(verbose=options.verbose)
1060 selftest()
1061+ if options.testsuite:
1062+ runner = run_tests
1063+ else:
1064+ runner = input_file
1065 start_time = time.time()
1066 for path in args:
1067 if os.path.isdir(path):
1068- input_dir(path)
1069- else:
1070- input_file(path)
1071+ input_dir(path, runner=runner)
1072+ elif not excluded(path):
1073+ options.counters['files'] += 1
1074+ runner(path)
1075 elapsed = time.time() - start_time
1076 if options.statistics:
1077 print_statistics()
1078 if options.benchmark:
1079 print_benchmark(elapsed)
1080- if options.count:
1081- count = get_count()
1082- if count:
1083+ count = get_count()
1084+ if count:
1085+ if options.count:
1086 sys.stderr.write(str(count) + '\n')
1087- sys.exit(1)
1088+ sys.exit(1)
1089
1090
1091 if __name__ == '__main__':
1092
1093=== modified file 'setup.py'
1094--- setup.py 2010-03-16 19:18:00 +0000
1095+++ setup.py 2011-08-05 17:38:30 +0000
1096@@ -1,6 +1,6 @@
1097 from setuptools import setup, find_packages
1098
1099-version = '0.5.0'
1100+version = '0.6.1'
1101 long_description = '\n\n'.join([open('README.rst').read(),
1102 open('CHANGES.txt').read(),
1103 open('TODO.txt').read()])

Subscribers

People subscribed via source and target branches

to all changes: