diff -Nru pep8-1.3.3/CHANGES.txt pep8-1.4.4/CHANGES.txt --- pep8-1.3.3/CHANGES.txt 2012-06-27 06:35:35.000000000 +0000 +++ pep8-1.4.4/CHANGES.txt 2013-02-24 15:26:38.000000000 +0000 @@ -2,6 +2,117 @@ ========= +1.4.4 (2013-02-24) +------------------ + +* Report E227 or E228 instead of E225 for whitespace around bitwise, shift + or modulo operators. (Issue #166) + +* Change the message for E226 to make clear that it is about arithmetic + operators. + +* Fix a false positive E128 for continuation line indentation with tabs. + +* Fix regression with the ``--diff`` option. (Issue #169) + +* Fix the ``TestReport`` class to print the unexpected warnings and + errors. + + +1.4.3 (2013-02-22) +------------------ + +* Hide the ``--doctest`` and ``--testsuite`` options when installed. + +* Fix crash with AST checkers when the syntax is invalid. (Issue #160) + +* Read from standard input if no path is specified. + +* Initiate a graceful shutdown on ``Control+C``. + +* Allow to change the ``checker_class`` for the ``StyleGuide``. + + +1.4.2 (2013-02-10) +------------------ + +* Support AST checkers provided by third-party applications. + +* Register new checkers with ``register_check(func_or_cls, codes)``. + +* Allow to construct a ``StyleGuide`` with a custom parser. + +* Accept visual indentation without parenthesis after the ``if`` + statement. (Issue #151) + +* Fix UnboundLocalError when using ``# noqa`` with continued lines. + (Issue #158) + +* Re-order the lines for the ``StandardReport``. + +* Expand tabs when checking E12 continuation lines. (Issue #155) + +* Refactor the testing class ``TestReport`` and the specific test + functions into a separate test module. + + +1.4.1 (2013-01-18) +------------------ + +* Allow sphinx.ext.autodoc syntax for comments. (Issue #110) + +* Report E703 instead of E702 for the trailing semicolon. (Issue #117) + +* Honor ``# noqa`` in addition to ``# nopep8``. (Issue #149) + +* Expose the ``OptionParser`` factory for better extensibility. + + +1.4 (2012-12-22) +---------------- + +* Report E226 instead of E225 for optional whitespace around common + operators (``*``, ``**``, ``/``, ``+`` and ``-``). This new error + code is ignored in the default configuration because PEP 8 recommends + to "use your own judgement". (Issue #96) + +* Lines with a ``# nopep8`` at the end will not issue errors on line + length E501 or continuation line indentation E12*. (Issue #27) + +* Fix AssertionError when the source file contains an invalid line + ending ``"\r\r\n"``. (Issue #119) + +* Read the ``[pep8]`` section of ``tox.ini`` or ``setup.cfg`` if present. + (Issue #93 and #141) + +* Add the Sphinx-based documentation, and publish it + on http://pep8.readthedocs.org/. (Issue #105) + + +1.3.4 (2012-12-18) +------------------ + +* Fix false positive E124 and E128 with comments. (Issue #100) + +* Fix error on stdin when running with bpython. (Issue #101) + +* Fix false positive E401. (Issue #104) + +* Report E231 for nested dictionary in list. (Issue #142) + +* Catch E271 at the beginning of the line. (Issue #133) + +* Fix false positive E126 for multi-line comments. (Issue #138) + +* Fix false positive E221 when operator is preceded by a comma. (Issue #135) + +* Fix ``--diff`` failing on one-line hunk. (Issue #137) + +* Fix the ``--exclude`` switch for directory paths. (Issue #111) + +* Use ``-`` filename to read from standard input. (Issue #128) + + 1.3.3 (2012-06-27) ------------------ diff -Nru pep8-1.3.3/MANIFEST.in pep8-1.4.4/MANIFEST.in --- pep8-1.3.3/MANIFEST.in 2012-04-06 15:36:29.000000000 +0000 +++ pep8-1.4.4/MANIFEST.in 2013-02-09 21:25:59.000000000 +0000 @@ -1,4 +1,9 @@ -recursive-include testsuite * -include pep8.py include *.txt include *.rst +recursive-include docs * +recursive-include testsuite * +recursive-exclude docs *.pyc +recursive-exclude docs *.pyo +recursive-exclude testsuite *.pyc +recursive-exclude testsuite *.pyo +prune docs/_build diff -Nru pep8-1.3.3/PKG-INFO pep8-1.4.4/PKG-INFO --- pep8-1.3.3/PKG-INFO 2012-06-27 06:36:55.000000000 +0000 +++ pep8-1.4.4/PKG-INFO 2013-02-24 15:29:45.000000000 +0000 @@ -1,8 +1,8 @@ Metadata-Version: 1.1 Name: pep8 -Version: 1.3.3 +Version: 1.4.4 Summary: Python style guide checker -Home-page: http://github.com/jcrocholl/pep8 +Home-page: http://pep8.readthedocs.org/ Author: Johann C. Rocholl Author-email: johann@rocholl.net License: Expat license @@ -15,11 +15,6 @@ .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ - Mailing List - ------------ - http://groups.google.com/group/pep8 - - Features -------- @@ -42,9 +37,7 @@ $ pip uninstall pep8 There's also a package for Debian/Ubuntu, but it's not always the - latest version:: - - $ sudo apt-get install pep8 + latest version. Example usage and output ------------------------ @@ -90,69 +83,131 @@ 612 W601 .has_key() is deprecated, use 'in' 1188 W602 deprecated form of raising exception - Quick help is available on the command line:: + Links + ----- - $ pep8 -h - Usage: pep8 [options] input ... + .. image:: https://api.travis-ci.org/jcrocholl/pep8.png?branch=master + :target: https://travis-ci.org/jcrocholl/pep8 + :alt: Build status - Options: - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose print status messages, or debug with -vv - -q, --quiet report only file names, or nothing with -qq - -r, --repeat (obsolete) show all occurrences of the same error - --first show first occurrence of each error - --exclude=patterns exclude files or directories which match these comma - separated patterns (default: .svn,CVS,.bzr,.hg,.git) - --filename=patterns when parsing directories, only check filenames matching - these comma separated patterns (default: *.py) - --select=errors select errors and warnings (e.g. E,W6) - --ignore=errors skip errors and warnings (e.g. E4,W) - --show-source show source code for each error - --show-pep8 show text of PEP 8 for each error (implies --first) - --statistics count errors and warnings - --count print total number of errors and warnings to standard - error and set exit code to 1 if total is not null - --max-line-length=n set maximum allowed line length (default: 79) - --format=format set the error format [default|pylint|] - --diff report only lines changed according to the unified diff - received on STDIN - - Testing Options: - --testsuite=dir run regression tests from dir - --doctest run doctest on myself - --benchmark measure processing speed - - Configuration: - The project options are read from the [pep8] section of the .pep8 file - located in any parent folder of the path(s) being processed. Allowed - options are: exclude, filename, select, ignore, max-line-length, - count, format, quiet, show-pep8, show-source, statistics, verbose. + * `Read the documentation `_ - --config=path config file location (default: /home/user/.config/pep8) + * `Fork me on GitHub `_ - Feedback - -------- - Your feedback is more than welcome. Write email to - johann@rocholl.net or post bugs and feature requests on github: + Changelog + ========= - http://github.com/jcrocholl/pep8/issues - Source download - --------------- + 1.4.4 (2013-02-24) + ------------------ - .. image:: https://secure.travis-ci.org/jcrocholl/pep8.png?branch=master - :target: https://secure.travis-ci.org/jcrocholl/pep8 - :alt: Build status + * Report E227 or E228 instead of E225 for whitespace around bitwise, shift + or modulo operators. (Issue #166) - The source code is currently available on github. Fork away! + * Change the message for E226 to make clear that it is about arithmetic + operators. - http://github.com/jcrocholl/pep8/ + * Fix a false positive E128 for continuation line indentation with tabs. + * Fix regression with the ``--diff`` option. (Issue #169) - Changelog - ========= + * Fix the ``TestReport`` class to print the unexpected warnings and + errors. + + + 1.4.3 (2013-02-22) + ------------------ + + * Hide the ``--doctest`` and ``--testsuite`` options when installed. + + * Fix crash with AST checkers when the syntax is invalid. (Issue #160) + + * Read from standard input if no path is specified. + + * Initiate a graceful shutdown on ``Control+C``. + + * Allow to change the ``checker_class`` for the ``StyleGuide``. + + + 1.4.2 (2013-02-10) + ------------------ + + * Support AST checkers provided by third-party applications. + + * Register new checkers with ``register_check(func_or_cls, codes)``. + + * Allow to construct a ``StyleGuide`` with a custom parser. + + * Accept visual indentation without parenthesis after the ``if`` + statement. (Issue #151) + + * Fix UnboundLocalError when using ``# noqa`` with continued lines. + (Issue #158) + + * Re-order the lines for the ``StandardReport``. + + * Expand tabs when checking E12 continuation lines. (Issue #155) + + * Refactor the testing class ``TestReport`` and the specific test + functions into a separate test module. + + + 1.4.1 (2013-01-18) + ------------------ + + * Allow sphinx.ext.autodoc syntax for comments. (Issue #110) + + * Report E703 instead of E702 for the trailing semicolon. (Issue #117) + + * Honor ``# noqa`` in addition to ``# nopep8``. (Issue #149) + + * Expose the ``OptionParser`` factory for better extensibility. + + + 1.4 (2012-12-22) + ---------------- + + * Report E226 instead of E225 for optional whitespace around common + operators (``*``, ``**``, ``/``, ``+`` and ``-``). This new error + code is ignored in the default configuration because PEP 8 recommends + to "use your own judgement". (Issue #96) + + * Lines with a ``# nopep8`` at the end will not issue errors on line + length E501 or continuation line indentation E12*. (Issue #27) + + * Fix AssertionError when the source file contains an invalid line + ending ``"\r\r\n"``. (Issue #119) + + * Read the ``[pep8]`` section of ``tox.ini`` or ``setup.cfg`` if present. + (Issue #93 and #141) + + * Add the Sphinx-based documentation, and publish it + on http://pep8.readthedocs.org/. (Issue #105) + + + 1.3.4 (2012-12-18) + ------------------ + + * Fix false positive E124 and E128 with comments. (Issue #100) + + * Fix error on stdin when running with bpython. (Issue #101) + + * Fix false positive E401. (Issue #104) + + * Report E231 for nested dictionary in list. (Issue #142) + + * Catch E271 at the beginning of the line. (Issue #133) + + * Fix false positive E126 for multi-line comments. (Issue #138) + + * Fix false positive E221 when operator is preceded by a comma. (Issue #135) + + * Fix ``--diff`` failing on one-line hunk. (Issue #137) + + * Fix the ``--exclude`` switch for directory paths. (Issue #111) + + * Use ``-`` filename to read from standard input. (Issue #128) 1.3.3 (2012-06-27) diff -Nru pep8-1.3.3/README.rst pep8-1.4.4/README.rst --- pep8-1.3.3/README.rst 2012-06-26 18:42:11.000000000 +0000 +++ pep8-1.4.4/README.rst 2012-12-22 21:16:58.000000000 +0000 @@ -7,11 +7,6 @@ .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ -Mailing List ------------- -http://groups.google.com/group/pep8 - - Features -------- @@ -34,9 +29,7 @@ $ pip uninstall pep8 There's also a package for Debian/Ubuntu, but it's not always the -latest version:: - - $ sudo apt-get install pep8 +latest version. Example usage and output ------------------------ @@ -82,62 +75,13 @@ 612 W601 .has_key() is deprecated, use 'in' 1188 W602 deprecated form of raising exception -Quick help is available on the command line:: - - $ pep8 -h - Usage: pep8 [options] input ... - - Options: - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose print status messages, or debug with -vv - -q, --quiet report only file names, or nothing with -qq - -r, --repeat (obsolete) show all occurrences of the same error - --first show first occurrence of each error - --exclude=patterns exclude files or directories which match these comma - separated patterns (default: .svn,CVS,.bzr,.hg,.git) - --filename=patterns when parsing directories, only check filenames matching - these comma separated patterns (default: *.py) - --select=errors select errors and warnings (e.g. E,W6) - --ignore=errors skip errors and warnings (e.g. E4,W) - --show-source show source code for each error - --show-pep8 show text of PEP 8 for each error (implies --first) - --statistics count errors and warnings - --count print total number of errors and warnings to standard - error and set exit code to 1 if total is not null - --max-line-length=n set maximum allowed line length (default: 79) - --format=format set the error format [default|pylint|] - --diff report only lines changed according to the unified diff - received on STDIN - - Testing Options: - --testsuite=dir run regression tests from dir - --doctest run doctest on myself - --benchmark measure processing speed - - Configuration: - The project options are read from the [pep8] section of the .pep8 file - located in any parent folder of the path(s) being processed. Allowed - options are: exclude, filename, select, ignore, max-line-length, - count, format, quiet, show-pep8, show-source, statistics, verbose. - - --config=path config file location (default: /home/user/.config/pep8) - -Feedback --------- - -Your feedback is more than welcome. Write email to -johann@rocholl.net or post bugs and feature requests on github: - -http://github.com/jcrocholl/pep8/issues - -Source download ---------------- +Links +----- -.. image:: https://secure.travis-ci.org/jcrocholl/pep8.png?branch=master - :target: https://secure.travis-ci.org/jcrocholl/pep8 +.. image:: https://api.travis-ci.org/jcrocholl/pep8.png?branch=master + :target: https://travis-ci.org/jcrocholl/pep8 :alt: Build status -The source code is currently available on github. Fork away! +* `Read the documentation `_ -http://github.com/jcrocholl/pep8/ +* `Fork me on GitHub `_ diff -Nru pep8-1.3.3/debian/changelog pep8-1.4.4/debian/changelog --- pep8-1.3.3/debian/changelog 2013-05-21 17:01:40.000000000 +0000 +++ pep8-1.4.4/debian/changelog 2013-05-21 17:01:40.000000000 +0000 @@ -1,8 +1,8 @@ -pep8 (1.3.3-0ubuntu1) raring; urgency=low +pep8 (1.4.4-1~raring1) raring; urgency=low - * New upstream release. + * New upstream release. (Closes: #694443) - -- Chuck Short Tue, 20 Nov 2012 10:07:43 -0600 + -- David Watson Wed, 06 Mar 2013 13:52:24 +0000 pep8 (1.2-1) unstable; urgency=low diff -Nru pep8-1.3.3/debian/control pep8-1.4.4/debian/control --- pep8-1.3.3/debian/control 2013-05-21 17:01:40.000000000 +0000 +++ pep8-1.4.4/debian/control 2013-05-21 17:01:40.000000000 +0000 @@ -1,8 +1,7 @@ Source: pep8 Section: python Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: David Watson +Maintainer: David Watson Build-Depends: debhelper (>= 7), python-all, python-setuptools Standards-Version: 3.9.3 Homepage: http://pypi.python.org/pypi/pep8 diff -Nru pep8-1.3.3/debian/docs pep8-1.4.4/debian/docs --- pep8-1.3.3/debian/docs 2013-05-21 17:01:40.000000000 +0000 +++ pep8-1.4.4/debian/docs 2013-05-21 17:01:40.000000000 +0000 @@ -1,2 +1 @@ README.rst -TODO.txt diff -Nru pep8-1.3.3/docs/Makefile pep8-1.4.4/docs/Makefile --- pep8-1.3.3/docs/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/Makefile 2012-12-20 18:17:22.000000000 +0000 @@ -0,0 +1,153 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pep8.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pep8.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/pep8" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pep8" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff -Nru pep8-1.3.3/docs/advanced.rst pep8-1.4.4/docs/advanced.rst --- pep8-1.3.3/docs/advanced.rst 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/advanced.rst 2012-12-22 16:00:18.000000000 +0000 @@ -0,0 +1,77 @@ +.. currentmodule:: pep8 + +============== +Advanced usage +============== + + +Automated tests +--------------- + +You can also execute `pep8` tests from Python code. For example, this +can be highly useful for automated testing of coding style conformance +in your project:: + + import unittest + import pep8 + + + class TestCodeFormat(unittest.TestCase): + + def test_pep8_conformance(self): + """Test that we conform to PEP8.""" + pep8style = pep8.StyleGuide(quiet=True) + result = pep8style.check_files(['file1.py', 'file2.py']) + self.assertEqual(result.total_errors, 0, + "Found code style errors (and warnings).") + +If you are using `nosetests` for running tests, remove `quiet=True` +since Nose suppresses stdout. + +There's also a shortcut for checking a single file:: + + import pep8 + + fchecker = pep8.Checker('testsuite/E27.py', show_source=True) + file_errors = fchecker.check_all() + + print("Found %s errors (and warnings)" % file_errors) + + +Skip file header +---------------- + +Another example is related to the `feature request #143 +`_: skip a number of lines +at the beginning and the end of a file. This use case is easy to implement +through a custom wrapper for the PEP 8 library:: + + #!python + import pep8 + + LINES_SLICE = slice(14, -20) + + class PEP8(pep8.StyleGuide): + """This subclass of pep8.StyleGuide will skip the first and last lines + of each file.""" + + def input_file(self, filename, lines=None, expected=None, line_offset=0): + if lines is None: + assert line_offset == 0 + line_offset = LINES_SLICE.start or 0 + lines = pep8.readlines(filename)[LINES_SLICE] + return super(PEP8, self).input_file( + filename, lines=lines, expected=expected, line_offset=line_offset) + + if __name__ == '__main__': + pep8style = PEP8(parse_argv=True, config_file=True) + report = pep8style.check_files() + if report.total_errors: + raise SystemExit(1) + +This module declares a lines' window which skips 14 lines at the beginning +and 20 lines at the end. If there's no line to skip at the end, it could be +changed with ``LINES_SLICE = slice(14, None)`` for example. + +You can save it in a file and use it with the same options as the +original ``pep8``. diff -Nru pep8-1.3.3/docs/api.rst pep8-1.4.4/docs/api.rst --- pep8-1.3.3/docs/api.rst 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/api.rst 2013-02-14 19:03:13.000000000 +0000 @@ -0,0 +1,89 @@ +======== +pep8 API +======== + +.. module:: pep8 + +The library provides classes which are usable by third party tools. + +.. contents:: + :local: + + +.. _main_classes: + +Checker Classes +--------------- + +The :class:`StyleGuide` class is used to configure a style guide checker +instance to check multiple files. + +The :class:`Checker` class can be used to check a single file. + + +.. autoclass:: StyleGuide(parse_argv=False, config_file=None, parser=None, paths=None, report=None, **kwargs) + + .. automethod:: init_report(reporter=None) + .. automethod:: check_files(paths=None) + .. automethod:: input_file(filename, lines=None, expected=None, line_offset=0) + .. automethod:: input_dir(dirname) + .. automethod:: excluded(filename) + .. automethod:: ignore_code(code) + .. automethod:: get_checks(argument_name) + +.. autoclass:: Checker(filename=None, lines=None, report=None, **kwargs) + + .. automethod:: readline + .. automethod:: readline_check_physical + .. automethod:: run_check(check, argument_names) + .. automethod:: check_physical(line) + .. automethod:: build_tokens_line + .. automethod:: check_logical + .. automethod:: check_ast + .. automethod:: generate_tokens + .. automethod:: check_all(expected=None, line_offset=0) + + +.. _report_classes: + +Report Classes +-------------- + +.. autoclass:: BaseReport(options) + + .. automethod:: start + .. automethod:: stop + .. automethod:: init_file(filename, lines, expected, line_offset) + .. automethod:: increment_logical_line + .. automethod:: error(line_number, offset, text, check) + .. automethod:: get_file_results + .. automethod:: get_count(prefix='') + .. automethod:: get_statistics(prefix='') + .. automethod:: print_statistics(prefix='') + .. automethod:: print_benchmark + +.. autoclass:: FileReport + +.. autoclass:: StandardReport + +.. autoclass:: DiffReport + + +Utilities +--------- + +.. autofunction:: expand_indent(line) +.. autofunction:: mute_string(text) +.. autofunction:: read_config(options, args, arglist, parser) +.. autofunction:: process_options(arglist=None, parse_argv=False, config_file=None) +.. autofunction:: register_check(func_or_cls, codes=None) + +.. + These ones are used internally, but they don't need advertising + .. autofunction:: readlines(filename) + .. autofunction:: isidentifier(word) + .. autofunction:: stdin_get_value() + .. autofunction:: parse_udiff(diff, patterns=None, parent='.') + .. autofunction:: filename_match(filename, patterns, default=True) + .. autofunction:: get_parser(prog='pep8', version=pep8.__version__) + .. autofunction:: init_checks_registry() diff -Nru pep8-1.3.3/docs/conf.py pep8-1.4.4/docs/conf.py --- pep8-1.3.3/docs/conf.py 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/conf.py 2013-02-23 10:31:01.000000000 +0000 @@ -0,0 +1,250 @@ +# -*- coding: utf-8 -*- +# +# pep8 documentation build configuration file, created by +# sphinx-quickstart on Tue Aug 21 09:47:49 2012. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration ---------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', + 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pep8' +copyright = u'2012-2013, Florent Xicluna' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# + +pep8_version = __import__('pep8').__version__.split('.') +# The short X.Y version. +version = '.'.join(pep8_version[:2]) +# The full version, including alpha/beta/rc tags. +release = '.'.join(pep8_version) + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for +# all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output -------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pep8doc' + + +# -- Options for LaTeX output ------------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + #'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'pep8.tex', u'pep8 documentation', + u'Florent Xicluna', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output ------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pep8', u'pep8 documentation', + [u'Florent Xicluna'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ----------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'pep8', u'pep8 documentation', u'Florent Xicluna', + 'pep8', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' diff -Nru pep8-1.3.3/docs/developer.rst pep8-1.4.4/docs/developer.rst --- pep8-1.3.3/docs/developer.rst 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/developer.rst 2012-12-22 16:36:17.000000000 +0000 @@ -0,0 +1,89 @@ +.. currentmodule:: pep8 + +================= +Developer's notes +================= + + +Source code +~~~~~~~~~~~ + +The source code is currently `available on GitHub`_ under the terms and +conditions of the :ref:`Expat license `. Fork away! + +* `Source code `_ and + `issue tracker `_ on GitHub. +* `Continuous tests `_ against Python + 2.5 through 3.3 and PyPy, on `Travis-CI platform + `_. + +.. _available on GitHub: https://github.com/jcrocholl/pep8 + + +Contribute +~~~~~~~~~~ + +You can add checks to this program by writing plugins. Each plugin is +a simple function that is called for each line of source code, either +physical or logical. + +Physical line: + +* Raw line of text from the input file. + +Logical line: + +* Multi-line statements converted to a single line. +* Stripped left and right. +* Contents of strings replaced with ``"xxx"`` of same length. +* Comments removed. + +The check function requests physical or logical lines by the name of +the first argument:: + + def maximum_line_length(physical_line) + def extraneous_whitespace(logical_line) + def blank_lines(logical_line, blank_lines, indent_level, line_number) + +The last example above demonstrates how check plugins can request +additional information with extra arguments. All attributes of the +:class:`Checker` object are available. Some examples: + +* ``lines``: a list of the raw lines from the input file +* ``tokens``: the tokens that contribute to this logical line +* ``line_number``: line number in the input file +* ``blank_lines``: blank lines before this one +* ``indent_char``: first indentation character in this file (``" "`` or ``"\t"``) +* ``indent_level``: indentation (with tabs expanded to multiples of 8) +* ``previous_indent_level``: indentation on previous line +* ``previous_logical``: previous logical line + +The docstring of each check function shall be the relevant part of +text from `PEP 8`_. It is printed if the user enables ``--show-pep8``. +Several docstrings contain examples directly from the `PEP 8`_ document. + +:: + + Okay: spam(ham[1], {eggs: 2}) + E201: spam( ham[1], {eggs: 2}) + +These examples are verified automatically when pep8.py is run with the +``--doctest`` option. You can add examples for your own check functions. +The format is simple: ``"Okay"`` or error/warning code followed by colon +and space, the rest of the line is example source code. If you put ``'r'`` +before the docstring, you can use ``\n`` for newline and ``\t`` for tab. + +Then be sure to pass the tests:: + + $ python pep8.py --testsuite testsuite + $ python pep8.py --doctest + $ python pep8.py --verbose pep8.py + +.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ + + +Changes +~~~~~~~ + +.. include:: ../CHANGES.txt + :start-line: 3 diff -Nru pep8-1.3.3/docs/index.rst pep8-1.4.4/docs/index.rst --- pep8-1.3.3/docs/index.rst 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/index.rst 2012-12-22 16:42:27.000000000 +0000 @@ -0,0 +1,69 @@ +.. pep8 documentation master file + +pep8's documentation +==================== + +*Python style guide checker* + +pep8 is a tool to check your Python code against some of the style +conventions in `PEP 8`_. + +.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ + + +Contents: + +.. toctree:: + :maxdepth: 2 + + intro + advanced + API + developer + +* Online documentation: http://pep8.readthedocs.org/ +* Source code and issue tracker: https://github.com/jcrocholl/pep8 + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` + + +Credits +======= + +Created by Johann C. Rocholl. + +Maintained by Florent Xicluna. + + +.. _license: + +License +======= + +The ``pep8`` library is provided under the terms and conditions of the +Expat license:: + + # Permission is hereby granted, free of charge, to any person + # obtaining a copy of this software and associated documentation files + # (the "Software"), to deal in the Software without restriction, + # including without limitation the rights to use, copy, modify, merge, + # publish, distribute, sublicense, and/or sell copies of the Software, + # and to permit persons to whom the Software is furnished to do so, + # subject to the following conditions: + # + # The above copyright notice and this permission notice shall be + # included in all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + # SOFTWARE. diff -Nru pep8-1.3.3/docs/intro.rst pep8-1.4.4/docs/intro.rst --- pep8-1.3.3/docs/intro.rst 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/intro.rst 2013-02-24 11:07:58.000000000 +0000 @@ -0,0 +1,361 @@ +.. currentmodule:: pep8 + +Introduction +============ + +pep8 is a tool to check your Python code against some of the style +conventions in `PEP 8`_. + +.. contents:: + :local: + + +Features +-------- + +* Plugin architecture: Adding new checks is easy. + +* Parseable output: Jump to error location in your editor. + +* Small: Just one Python file, requires only stdlib. You can use just + the pep8.py file for this purpose. + +* Comes with a comprehensive test suite. + + +Disclaimer +---------- + +This utility does not enforce every single rule of PEP 8. It helps to +verify that some coding conventions are applied but it does not intend +to be exhaustive. Some rules cannot be expressed with a simple algorithm, +and other rules are only guidelines which you could circumvent when you +need to. + +Always remember this statement from `PEP 8`_: + + *A style guide is about consistency. Consistency with this style guide is + important. Consistency within a project is more important. Consistency + within one module or function is most important.* + + +Among other things, these features are currently not in the scope of +the ``pep8`` library: + +* **naming conventions**: this kind of feature is supported through plugins. + Install `flake8 `_ and the + `pep8-naming extension `_ to use + this feature. +* **docstring conventions**: they are not in the scope of this library; + see the `pep257 project `_. +* **automatic fixing**: see the section *PEP8 Fixers* in the + :ref:`related tools ` page. + + +Installation +------------ + +You can install, upgrade, uninstall pep8.py with these commands:: + + $ pip install pep8 + $ pip install --upgrade pep8 + $ pip uninstall pep8 + +There's also a package for Debian/Ubuntu, but it's not always the +latest version:: + + $ sudo apt-get install pep8 + + +Example usage and output +------------------------ + +:: + + $ pep8 --first optparse.py + optparse.py:69:11: E401 multiple imports on one line + optparse.py:77:1: E302 expected 2 blank lines, found 1 + optparse.py:88:5: E301 expected 1 blank line, found 0 + optparse.py:222:34: W602 deprecated form of raising exception + optparse.py:347:31: E211 whitespace before '(' + optparse.py:357:17: E201 whitespace after '{' + optparse.py:472:29: E221 multiple spaces before operator + optparse.py:544:21: W601 .has_key() is deprecated, use 'in' + +You can also make pep8.py show the source code for each error, and +even the relevant text from PEP 8:: + + $ pep8 --show-source --show-pep8 testsuite/E40.py + testsuite/E40.py:2:10: E401 multiple imports on one line + import os, sys + ^ + Imports should usually be on separate lines. + + Okay: import os\nimport sys + E401: import sys, os + + +Or you can display how often each error was found:: + + $ pep8 --statistics -qq Python-2.5/Lib + 232 E201 whitespace after '[' + 599 E202 whitespace before ')' + 631 E203 whitespace before ',' + 842 E211 whitespace before '(' + 2531 E221 multiple spaces before operator + 4473 E301 expected 1 blank line, found 0 + 4006 E302 expected 2 blank lines, found 1 + 165 E303 too many blank lines (4) + 325 E401 multiple imports on one line + 3615 E501 line too long (82 characters) + 612 W601 .has_key() is deprecated, use 'in' + 1188 W602 deprecated form of raising exception + +Quick help is available on the command line:: + + $ pep8 -h + Usage: pep8 [options] input ... + + Options: + --version show program's version number and exit + -h, --help show this help message and exit + -v, --verbose print status messages, or debug with -vv + -q, --quiet report only file names, or nothing with -qq + --first show first occurrence of each error + --exclude=patterns exclude files or directories which match these comma + separated patterns (default: .svn,CVS,.bzr,.hg,.git) + --filename=patterns when parsing directories, only check filenames matching + these comma separated patterns (default: *.py) + --select=errors select errors and warnings (e.g. E,W6) + --ignore=errors skip errors and warnings (e.g. E4,W) + --show-source show source code for each error + --show-pep8 show text of PEP 8 for each error (implies --first) + --statistics count errors and warnings + --count print total number of errors and warnings to standard + error and set exit code to 1 if total is not null + --max-line-length=n set maximum allowed line length (default: 79) + --format=format set the error format [default|pylint|] + --diff report only lines changed according to the unified diff + received on STDIN + + Testing Options: + --benchmark measure processing speed + + Configuration: + The project options are read from the [pep8] section of the tox.ini + file or the setup.cfg file located in any parent folder of the path(s) + being processed. Allowed options are: exclude, filename, select, + ignore, max-line-length, count, format, quiet, show-pep8, show-source, + statistics, verbose. + + --config=path user config file location (default: ~/.config/pep8) + + +Configuration +------------- + +The behaviour may be configured at two levels. + +The user settings are read from the ``~/.config/pep8`` file. +Example:: + + [pep8] + ignore = E226,E302,E41 + max-line-length = 160 + +At the project level, a ``.pep8`` file, a ``tox.ini`` file or a ``setup.cfg`` +file is read if present. Only the first file is considered. If this file +does not have a ``[pep8]`` section, no project specific configuration is +loaded. + +If the ``ignore`` option is not in the configuration and not in the arguments, +only the error codes ``E226`` and ``E241/E242`` are ignored (see below). + + +Error codes +----------- + +This is the current list of error and warning codes: + ++----------+----------------------------------------------------------------------+ +| code | sample message | ++==========+======================================================================+ +| **E1** | *Indentation* | ++----------+----------------------------------------------------------------------+ +| E101 | indentation contains mixed spaces and tabs | ++----------+----------------------------------------------------------------------+ +| E111 | indentation is not a multiple of four | ++----------+----------------------------------------------------------------------+ +| E112 | expected an indented block | ++----------+----------------------------------------------------------------------+ +| E113 | unexpected indentation | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E121 | continuation line indentation is not a multiple of four | ++----------+----------------------------------------------------------------------+ +| E122 | continuation line missing indentation or outdented | ++----------+----------------------------------------------------------------------+ +| E123 | closing bracket does not match indentation of opening bracket's line | ++----------+----------------------------------------------------------------------+ +| E124 | closing bracket does not match visual indentation | ++----------+----------------------------------------------------------------------+ +| E125 | continuation line does not distinguish itself from next logical line | ++----------+----------------------------------------------------------------------+ +| E126 | continuation line over-indented for hanging indent | ++----------+----------------------------------------------------------------------+ +| E127 | continuation line over-indented for visual indent | ++----------+----------------------------------------------------------------------+ +| E128 | continuation line under-indented for visual indent | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **E2** | *Whitespace* | ++----------+----------------------------------------------------------------------+ +| E201 | whitespace after '(' | ++----------+----------------------------------------------------------------------+ +| E202 | whitespace before ')' | ++----------+----------------------------------------------------------------------+ +| E203 | whitespace before ':' | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E211 | whitespace before '(' | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E221 | multiple spaces before operator | ++----------+----------------------------------------------------------------------+ +| E222 | multiple spaces after operator | ++----------+----------------------------------------------------------------------+ +| E223 | tab before operator | ++----------+----------------------------------------------------------------------+ +| E224 | tab after operator | ++----------+----------------------------------------------------------------------+ +| E225 | missing whitespace around operator | ++----------+----------------------------------------------------------------------+ +| E226 (*) | missing whitespace around arithmetic operator | ++----------+----------------------------------------------------------------------+ +| E227 | missing whitespace around bitwise or shift operator | ++----------+----------------------------------------------------------------------+ +| E228 | missing whitespace around modulo operator | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E231 | missing whitespace after ',' | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E241 (*) | multiple spaces after ',' | ++----------+----------------------------------------------------------------------+ +| E242 (*) | tab after ',' | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E251 | no spaces around keyword / parameter equals | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E261 | at least two spaces before inline comment | ++----------+----------------------------------------------------------------------+ +| E262 | inline comment should start with '# ' | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| E271 | multiple spaces after keyword | ++----------+----------------------------------------------------------------------+ +| E272 | multiple spaces before keyword | ++----------+----------------------------------------------------------------------+ +| E273 | tab after keyword | ++----------+----------------------------------------------------------------------+ +| E274 | tab before keyword | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **E3** | *Blank line* | ++----------+----------------------------------------------------------------------+ +| E301 | expected 1 blank line, found 0 | ++----------+----------------------------------------------------------------------+ +| E302 | expected 2 blank lines, found 0 | ++----------+----------------------------------------------------------------------+ +| E303 | too many blank lines (3) | ++----------+----------------------------------------------------------------------+ +| E304 | blank lines found after function decorator | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **E4** | *Import* | ++----------+----------------------------------------------------------------------+ +| E401 | multiple imports on one line | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **E5** | *Line length* | ++----------+----------------------------------------------------------------------+ +| E501 | line too long (82 > 79 characters) | ++----------+----------------------------------------------------------------------+ +| E502 | the backslash is redundant between brackets | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **E7** | *Statement* | ++----------+----------------------------------------------------------------------+ +| E701 | multiple statements on one line (colon) | ++----------+----------------------------------------------------------------------+ +| E702 | multiple statements on one line (semicolon) | ++----------+----------------------------------------------------------------------+ +| E703 | statement ends with a semicolon | ++----------+----------------------------------------------------------------------+ +| E711 | comparison to None should be 'if cond is None:' | ++----------+----------------------------------------------------------------------+ +| E712 | comparison to True should be 'if cond is True:' or 'if cond:' | ++----------+----------------------------------------------------------------------+ +| E721 | do not compare types, use 'isinstance()' | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **E9** | *Runtime* | ++----------+----------------------------------------------------------------------+ +| E901 | SyntaxError or IndentationError | ++----------+----------------------------------------------------------------------+ +| E902 | IOError | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **W1** | *Indentation warning* | ++----------+----------------------------------------------------------------------+ +| W191 | indentation contains tabs | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **W2** | *Whitespace warning* | ++----------+----------------------------------------------------------------------+ +| W291 | trailing whitespace | ++----------+----------------------------------------------------------------------+ +| W292 | no newline at end of file | ++----------+----------------------------------------------------------------------+ +| W293 | blank line contains whitespace | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **W3** | *Blank line warning* | ++----------+----------------------------------------------------------------------+ +| W391 | blank line at end of file | ++----------+----------------------------------------------------------------------+ ++----------+----------------------------------------------------------------------+ +| **W6** | *Deprecation warning* | ++----------+----------------------------------------------------------------------+ +| W601 | .has_key() is deprecated, use 'in' | ++----------+----------------------------------------------------------------------+ +| W602 | deprecated form of raising exception | ++----------+----------------------------------------------------------------------+ +| W603 | '<>' is deprecated, use '!=' | ++----------+----------------------------------------------------------------------+ +| W604 | backticks are deprecated, use 'repr()' | ++----------+----------------------------------------------------------------------+ + +**(*)** In the default configuration, the checks **E226**, **E241** +and **E242** are ignored because they are not rules unanimously accepted, +and `PEP 8`_ does not enforce them. + + +Note: most errors can be listed with such one-liner:: + + $ python pep8.py --first --select E,W testsuite/ --format '%(code)s: %(text)s' + + +.. _related-tools: + +Related tools +------------- + +The `flake8 checker `_ is a wrapper around +``pep8`` and similar tools. It supports plugins. + +Other tools which use ``pep8`` are referenced in the Wiki: `list of related tools +`_. + +.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ diff -Nru pep8-1.3.3/docs/make.bat pep8-1.4.4/docs/make.bat --- pep8-1.3.3/docs/make.bat 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/docs/make.bat 2012-12-20 18:17:50.000000000 +0000 @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pep8.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pep8.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff -Nru pep8-1.3.3/pep8.egg-info/PKG-INFO pep8-1.4.4/pep8.egg-info/PKG-INFO --- pep8-1.3.3/pep8.egg-info/PKG-INFO 2012-06-27 06:36:54.000000000 +0000 +++ pep8-1.4.4/pep8.egg-info/PKG-INFO 2013-02-24 15:29:45.000000000 +0000 @@ -1,8 +1,8 @@ Metadata-Version: 1.1 Name: pep8 -Version: 1.3.3 +Version: 1.4.4 Summary: Python style guide checker -Home-page: http://github.com/jcrocholl/pep8 +Home-page: http://pep8.readthedocs.org/ Author: Johann C. Rocholl Author-email: johann@rocholl.net License: Expat license @@ -15,11 +15,6 @@ .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ - Mailing List - ------------ - http://groups.google.com/group/pep8 - - Features -------- @@ -42,9 +37,7 @@ $ pip uninstall pep8 There's also a package for Debian/Ubuntu, but it's not always the - latest version:: - - $ sudo apt-get install pep8 + latest version. Example usage and output ------------------------ @@ -90,69 +83,131 @@ 612 W601 .has_key() is deprecated, use 'in' 1188 W602 deprecated form of raising exception - Quick help is available on the command line:: + Links + ----- - $ pep8 -h - Usage: pep8 [options] input ... + .. image:: https://api.travis-ci.org/jcrocholl/pep8.png?branch=master + :target: https://travis-ci.org/jcrocholl/pep8 + :alt: Build status - Options: - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose print status messages, or debug with -vv - -q, --quiet report only file names, or nothing with -qq - -r, --repeat (obsolete) show all occurrences of the same error - --first show first occurrence of each error - --exclude=patterns exclude files or directories which match these comma - separated patterns (default: .svn,CVS,.bzr,.hg,.git) - --filename=patterns when parsing directories, only check filenames matching - these comma separated patterns (default: *.py) - --select=errors select errors and warnings (e.g. E,W6) - --ignore=errors skip errors and warnings (e.g. E4,W) - --show-source show source code for each error - --show-pep8 show text of PEP 8 for each error (implies --first) - --statistics count errors and warnings - --count print total number of errors and warnings to standard - error and set exit code to 1 if total is not null - --max-line-length=n set maximum allowed line length (default: 79) - --format=format set the error format [default|pylint|] - --diff report only lines changed according to the unified diff - received on STDIN - - Testing Options: - --testsuite=dir run regression tests from dir - --doctest run doctest on myself - --benchmark measure processing speed - - Configuration: - The project options are read from the [pep8] section of the .pep8 file - located in any parent folder of the path(s) being processed. Allowed - options are: exclude, filename, select, ignore, max-line-length, - count, format, quiet, show-pep8, show-source, statistics, verbose. + * `Read the documentation `_ - --config=path config file location (default: /home/user/.config/pep8) + * `Fork me on GitHub `_ - Feedback - -------- - Your feedback is more than welcome. Write email to - johann@rocholl.net or post bugs and feature requests on github: + Changelog + ========= - http://github.com/jcrocholl/pep8/issues - Source download - --------------- + 1.4.4 (2013-02-24) + ------------------ - .. image:: https://secure.travis-ci.org/jcrocholl/pep8.png?branch=master - :target: https://secure.travis-ci.org/jcrocholl/pep8 - :alt: Build status + * Report E227 or E228 instead of E225 for whitespace around bitwise, shift + or modulo operators. (Issue #166) - The source code is currently available on github. Fork away! + * Change the message for E226 to make clear that it is about arithmetic + operators. - http://github.com/jcrocholl/pep8/ + * Fix a false positive E128 for continuation line indentation with tabs. + * Fix regression with the ``--diff`` option. (Issue #169) - Changelog - ========= + * Fix the ``TestReport`` class to print the unexpected warnings and + errors. + + + 1.4.3 (2013-02-22) + ------------------ + + * Hide the ``--doctest`` and ``--testsuite`` options when installed. + + * Fix crash with AST checkers when the syntax is invalid. (Issue #160) + + * Read from standard input if no path is specified. + + * Initiate a graceful shutdown on ``Control+C``. + + * Allow to change the ``checker_class`` for the ``StyleGuide``. + + + 1.4.2 (2013-02-10) + ------------------ + + * Support AST checkers provided by third-party applications. + + * Register new checkers with ``register_check(func_or_cls, codes)``. + + * Allow to construct a ``StyleGuide`` with a custom parser. + + * Accept visual indentation without parenthesis after the ``if`` + statement. (Issue #151) + + * Fix UnboundLocalError when using ``# noqa`` with continued lines. + (Issue #158) + + * Re-order the lines for the ``StandardReport``. + + * Expand tabs when checking E12 continuation lines. (Issue #155) + + * Refactor the testing class ``TestReport`` and the specific test + functions into a separate test module. + + + 1.4.1 (2013-01-18) + ------------------ + + * Allow sphinx.ext.autodoc syntax for comments. (Issue #110) + + * Report E703 instead of E702 for the trailing semicolon. (Issue #117) + + * Honor ``# noqa`` in addition to ``# nopep8``. (Issue #149) + + * Expose the ``OptionParser`` factory for better extensibility. + + + 1.4 (2012-12-22) + ---------------- + + * Report E226 instead of E225 for optional whitespace around common + operators (``*``, ``**``, ``/``, ``+`` and ``-``). This new error + code is ignored in the default configuration because PEP 8 recommends + to "use your own judgement". (Issue #96) + + * Lines with a ``# nopep8`` at the end will not issue errors on line + length E501 or continuation line indentation E12*. (Issue #27) + + * Fix AssertionError when the source file contains an invalid line + ending ``"\r\r\n"``. (Issue #119) + + * Read the ``[pep8]`` section of ``tox.ini`` or ``setup.cfg`` if present. + (Issue #93 and #141) + + * Add the Sphinx-based documentation, and publish it + on http://pep8.readthedocs.org/. (Issue #105) + + + 1.3.4 (2012-12-18) + ------------------ + + * Fix false positive E124 and E128 with comments. (Issue #100) + + * Fix error on stdin when running with bpython. (Issue #101) + + * Fix false positive E401. (Issue #104) + + * Report E231 for nested dictionary in list. (Issue #142) + + * Catch E271 at the beginning of the line. (Issue #133) + + * Fix false positive E126 for multi-line comments. (Issue #138) + + * Fix false positive E221 when operator is preceded by a comma. (Issue #135) + + * Fix ``--diff`` failing on one-line hunk. (Issue #137) + + * Fix the ``--exclude`` switch for directory paths. (Issue #111) + + * Use ``-`` filename to read from standard input. (Issue #128) 1.3.3 (2012-06-27) diff -Nru pep8-1.3.3/pep8.egg-info/SOURCES.txt pep8-1.4.4/pep8.egg-info/SOURCES.txt --- pep8-1.3.3/pep8.egg-info/SOURCES.txt 2012-06-27 06:36:55.000000000 +0000 +++ pep8-1.4.4/pep8.egg-info/SOURCES.txt 2013-05-21 17:01:40.000000000 +0000 @@ -1,9 +1,17 @@ CHANGES.txt MANIFEST.in README.rst -TODO.txt pep8.py +setup.cfg setup.py +docs/Makefile +docs/advanced.rst +docs/api.rst +docs/conf.py +docs/developer.rst +docs/index.rst +docs/intro.rst +docs/make.bat pep8.egg-info/PKG-INFO pep8.egg-info/SOURCES.txt pep8.egg-info/dependency_links.txt @@ -38,4 +46,5 @@ testsuite/W60.py testsuite/latin-1.py testsuite/python3.py +testsuite/test_pep8.py testsuite/utf-8.py \ No newline at end of file diff -Nru pep8-1.3.3/pep8.py pep8-1.4.4/pep8.py --- pep8-1.3.3/pep8.py 2012-06-27 06:35:59.000000000 +0000 +++ pep8-1.4.4/pep8.py 2013-02-24 15:27:10.000000000 +0000 @@ -1,6 +1,7 @@ #!/usr/bin/env python # pep8.py - Check Python source code formatting, according to PEP 8 -# Copyright (C) 2006 Johann C. Rocholl +# Copyright (C) 2006-2009 Johann C. Rocholl +# Copyright (C) 2009-2013 Florent Xicluna # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files @@ -43,57 +44,8 @@ 600 deprecation 700 statements 900 syntax error - -You can add checks to this program by writing plugins. Each plugin is -a simple function that is called for each line of source code, either -physical or logical. - -Physical line: -- Raw line of text from the input file. - -Logical line: -- Multi-line statements converted to a single line. -- Stripped left and right. -- Contents of strings replaced with 'xxx' of same length. -- Comments removed. - -The check function requests physical or logical lines by the name of -the first argument: - -def maximum_line_length(physical_line) -def extraneous_whitespace(logical_line) -def blank_lines(logical_line, blank_lines, indent_level, line_number) - -The last example above demonstrates how check plugins can request -additional information with extra arguments. All attributes of the -Checker object are available. Some examples: - -lines: a list of the raw lines from the input file -tokens: the tokens that contribute to this logical line -line_number: line number in the input file -blank_lines: blank lines before this one -indent_char: first indentation character in this file (' ' or '\t') -indent_level: indentation (with tabs expanded to multiples of 8) -previous_indent_level: indentation on previous line -previous_logical: previous logical line - -The docstring of each check function shall be the relevant part of -text from PEP 8. It is printed if the user enables --show-pep8. -Several docstrings contain examples directly from the PEP 8 document. - -Okay: spam(ham[1], {eggs: 2}) -E201: spam( ham[1], {eggs: 2}) - -These examples are verified automatically when pep8.py is run with the ---doctest option. You can add examples for your own check functions. -The format is simple: "Okay" or error/warning code followed by colon -and space, the rest of the line is example source code. If you put 'r' -before the docstring, you can use \n for newline, \t for tab and \s -for space. - """ - -__version__ = '1.3.3' +__version__ = '1.4.4' import os import sys @@ -111,27 +63,29 @@ from ConfigParser import RawConfigParser DEFAULT_EXCLUDE = '.svn,CVS,.bzr,.hg,.git' -DEFAULT_IGNORE = 'E24' +DEFAULT_IGNORE = 'E226,E24' if sys.platform == 'win32': DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8') else: DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or os.path.expanduser('~/.config'), 'pep8') +PROJECT_CONFIG = ('.pep8', 'tox.ini', 'setup.cfg') +TESTSUITE_PATH = os.path.join(os.path.dirname(__file__), 'testsuite') MAX_LINE_LENGTH = 79 REPORT_FORMAT = { 'default': '%(path)s:%(row)d:%(col)d: %(code)s %(text)s', 'pylint': '%(path)s:%(row)d: [%(code)s] %(text)s', } - +PyCF_ONLY_AST = 1024 SINGLETONS = frozenset(['False', 'None', 'True']) KEYWORDS = frozenset(keyword.kwlist + ['print']) - SINGLETONS -BINARY_OPERATORS = frozenset([ - '**=', '*=', '+=', '-=', '!=', '<>', - '%=', '^=', '&=', '|=', '==', '/=', '//=', '<=', '>=', '<<=', '>>=', - '%', '^', '&', '|', '=', '/', '//', '<', '>', '<<']) UNARY_OPERATORS = frozenset(['>>', '**', '*', '+', '-']) -OPERATORS = BINARY_OPERATORS | UNARY_OPERATORS +ARITHMETIC_OP = frozenset(['**', '*', '/', '//', '+', '-']) +WS_OPTIONAL_OPERATORS = ARITHMETIC_OP.union(['^', '&', '|', '<<', '>>', '%']) +WS_NEEDED_OPERATORS = frozenset([ + '**=', '*=', '/=', '//=', '+=', '-=', '!=', '<>', '<', '>', + '%=', '^=', '&=', '|=', '==', '<=', '>=', '<<=', '>>=', '=']) WHITESPACE = frozenset(' \t') SKIP_TOKENS = frozenset([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT]) @@ -140,19 +94,17 @@ INDENT_REGEX = re.compile(r'([ \t]*)') RAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*(,)') RERAISE_COMMA_REGEX = re.compile(r'raise\s+\w+\s*,\s*\w+\s*,\s*\w+') -SELFTEST_REGEX = re.compile(r'(Okay|[EW]\d{3}):\s(.*)') -ERRORCODE_REGEX = re.compile(r'[EW]\d{3}') +ERRORCODE_REGEX = re.compile(r'\b[A-Z]\d{3}\b') DOCSTRING_REGEX = re.compile(r'u?r?["\']') EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]') WHITESPACE_AFTER_COMMA_REGEX = re.compile(r'[,;:]\s*(?: |\t)') COMPARE_SINGLETON_REGEX = re.compile(r'([=!]=)\s*(None|False|True)') -COMPARE_TYPE_REGEX = re.compile(r'([=!]=|is|is\s+not)\s*type(?:s\.(\w+)Type' - r'|\(\s*(\(\s*\)|[^)]*[^ )])\s*\))') -KEYWORD_REGEX = re.compile(r'(?:[^\s])(\s*)\b(?:%s)\b(\s*)' % - r'|'.join(KEYWORDS)) -OPERATOR_REGEX = re.compile(r'(?:[^\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)') +COMPARE_TYPE_REGEX = re.compile(r'(?:[=!]=|is(?:\s+not)?)\s*type(?:s.\w+Type' + r'|\s*\(\s*([^)]*[^ )])\s*\))') +KEYWORD_REGEX = re.compile(r'(\s*)\b(?:%s)\b(\s*)' % r'|'.join(KEYWORDS)) +OPERATOR_REGEX = re.compile(r'(?:[^,\s])(\s*)(?:[-+*/|!<=>%&^]+)(\s*)') LAMBDA_REGEX = re.compile(r'\blambda\b') -HUNK_REGEX = re.compile(r'^@@ -\d+,\d+ \+(\d+),(\d+) @@.*$') +HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$') # Work around Python < 2.6 behaviour, which does not generate NL after # a comment which is on a line by itself. @@ -212,8 +164,8 @@ The warning returned varies on whether the line itself is blank, for easier filtering for those who want to indent their blank lines. - Okay: spam(1) - W291: spam(1)\s + Okay: spam(1)\n# + W291: spam(1) \n# W293: class Foo(object):\n \n bang = 12 """ physical_line = physical_line.rstrip('\n') # chr(10), newline @@ -264,6 +216,8 @@ line = physical_line.rstrip() length = len(line) if length > max_line_length: + if noqa(line): + return if hasattr(line, 'decode'): # Python 2 # The line could contain multi-byte characters try: @@ -387,13 +341,15 @@ Okay: a[1:4:2] E231: ['a','b'] E231: foo(bar,baz) + E231: [{'a':'b'}] """ line = logical_line for index in range(len(line) - 1): char = line[index] if char in ',;:' and line[index + 1] not in WHITESPACE: before = line[:index] - if char == ':' and before.count('[') > before.count(']'): + if char == ':' and before.count('[') > before.count(']') and \ + before.rfind('{') < before.rfind('['): continue # Slice syntax, no space required if char == ',' and line[index + 1] == ')': continue # Allow tuple with only one element: (3,) @@ -455,7 +411,7 @@ """ first_row = tokens[0][2][0] nrows = 1 + tokens[-1][2][0] - first_row - if nrows == 1: + if nrows == 1 or noqa(tokens[0][4]): return # indent_next tells us whether the next block is indented; assuming @@ -470,13 +426,14 @@ # relative indents of physical lines rel_indent = [0] * nrows # visual indents - indent = [indent_level] indent_chances = {} - last_indent = (0, 0) + last_indent = tokens[0][2] + indent = [last_indent[1]] if verbose >= 3: print(">>> " + tokens[0][4].rstrip()) for token_type, text, start, end, line in tokens: + newline = row < start[0] - first_row if newline: row = start[0] - first_row @@ -490,7 +447,7 @@ print("... " + line.rstrip()) # record the initial indent. - rel_indent[row] = start[1] - indent_level + rel_indent[row] = expand_indent(line) - indent_level if depth: # a bracket expression in a continuation line. @@ -508,11 +465,11 @@ # this line starts with a closing bracket if indent[depth]: if start[1] != indent[depth]: - yield (start, 'E124 closing bracket does not match ' - 'visual indentation') + yield (start, "E124 closing bracket does not match " + "visual indentation") elif hang: - yield (start, 'E123 closing bracket does not match ' - 'indentation of opening bracket\'s line') + yield (start, "E123 closing bracket does not match " + "indentation of opening bracket's line") elif visual_indent is True: # visual indent is verified if not indent[depth]: @@ -522,32 +479,37 @@ pass elif indent[depth] and start[1] < indent[depth]: # visual indent is broken - yield (start, 'E128 continuation line ' - 'under-indented for visual indent') + yield (start, "E128 continuation line " + "under-indented for visual indent") elif hang == 4 or (indent_next and rel_indent[row] == 8): # hanging indent is verified pass else: # indent is broken if hang <= 0: - error = 'E122', 'missing indentation or outdented' + error = "E122", "missing indentation or outdented" elif indent[depth]: - error = 'E127', 'over-indented for visual indent' + error = "E127", "over-indented for visual indent" elif hang % 4: - error = 'E121', 'indentation is not a multiple of four' + error = "E121", "indentation is not a multiple of four" else: - error = 'E126', 'over-indented for hanging indent' + error = "E126", "over-indented for hanging indent" yield start, "%s continuation line %s" % error # look for visual indenting - if parens[row] and token_type != tokenize.NL and not indent[depth]: + if (parens[row] and token_type not in (tokenize.NL, tokenize.COMMENT) + and not indent[depth]): indent[depth] = start[1] indent_chances[start[1]] = True if verbose >= 4: print("bracket depth %s indent to %s" % (depth, start[1])) # deal with implicit string concatenation - elif token_type == tokenize.STRING or text in ('u', 'ur', 'b', 'br'): + elif (token_type in (tokenize.STRING, tokenize.COMMENT) or + text in ('u', 'ur', 'b', 'br')): indent_chances[start[1]] = str + # special case for the "if" statement because len("if (") == 4 + elif not indent_chances and not row and not depth and text == 'if': + indent_chances[end[1] + 1] = True # keep track of bracket depth if token_type == tokenize.OP: @@ -664,20 +626,16 @@ Okay: hypot2 = x * x + y * y Okay: c = (a + b) * (a - b) Okay: foo(bar, key='word', *args, **kwargs) - Okay: baz(**kwargs) - Okay: negative = -1 - Okay: spam(-1) Okay: alpha[:-i] - Okay: if not -5 < x < +5:\n pass - Okay: lambda *args, **kw: (args, kw) E225: i=i+1 E225: submitted +=1 - E225: x = x*2 - 1 - E225: hypot2 = x*x + y*y - E225: c = (a+b) * (a-b) - E225: c = alpha -4 + E225: x = x /2 - 1 E225: z = x **y + E226: c = (a+b) * (a-b) + E226: hypot2 = x*x + y*y + E227: c = a|b + E228: msg = fmt%(errno, errmsg) """ parens = 0 need_space = False @@ -685,7 +643,7 @@ prev_text = prev_end = None for token_type, text, start, end, line in tokens: if token_type in (tokenize.NL, tokenize.NEWLINE, tokenize.ERRORTOKEN): - # ERRORTOKEN is triggered by backticks in Python 3000 + # ERRORTOKEN is triggered by backticks in Python 3 continue if text in ('(', 'lambda'): parens += 1 @@ -693,32 +651,59 @@ parens -= 1 if need_space: if start != prev_end: + # Found a (probably) needed space + if need_space is not True and not need_space[1]: + yield (need_space[0], + "E225 missing whitespace around operator") need_space = False elif text == '>' and prev_text in ('<', '-'): # Tolerate the "<>" operator, even if running Python 3 # Deal with Python 3's annotated return value "->" pass else: - yield prev_end, "E225 missing whitespace around operator" + if need_space is True or need_space[1]: + # A needed trailing space was not found + yield prev_end, "E225 missing whitespace around operator" + else: + code, optype = 'E226', 'arithmetic' + if prev_text == '%': + code, optype = 'E228', 'modulo' + elif prev_text not in ARITHMETIC_OP: + code, optype = 'E227', 'bitwise or shift' + yield (need_space[0], "%s missing whitespace " + "around %s operator" % (code, optype)) need_space = False elif token_type == tokenize.OP and prev_end is not None: if text == '=' and parens: # Allow keyword args or defaults: foo(bar=None). pass - elif text in BINARY_OPERATORS: + elif text in WS_NEEDED_OPERATORS: need_space = True elif text in UNARY_OPERATORS: + # Check if the operator is being used as a binary operator # Allow unary operators: -123, -x, +1. # Allow argument unpacking: foo(*args, **kwargs). if prev_type == tokenize.OP: - if prev_text in '}])': - need_space = True + binary_usage = (prev_text in '}])') elif prev_type == tokenize.NAME: - if prev_text not in KEYWORDS: + binary_usage = (prev_text not in KEYWORDS) + else: + binary_usage = (prev_type not in SKIP_TOKENS) + + if binary_usage: + if text in WS_OPTIONAL_OPERATORS: + need_space = None + else: need_space = True - elif prev_type not in SKIP_TOKENS: - need_space = True - if need_space and start == prev_end: + elif text in WS_OPTIONAL_OPERATORS: + need_space = None + + if need_space is None: + # Surrounding space is optional, but ensure that + # trailing space matches opening space + need_space = (prev_end, start != prev_end) + elif need_space and start == prev_end: + # A needed opening space was not found yield prev_end, "E225 missing whitespace around operator" need_space = False prev_type = token_type @@ -807,7 +792,8 @@ if prev_end[0] == start[0] and start[1] < prev_end[1] + 2: yield (prev_end, "E261 at least two spaces before inline comment") - if text.startswith('# ') or not text.startswith('# '): + symbol, sp, comment = text.partition(' ') + if symbol not in ('#', '#:') or comment[:1].isspace(): yield start, "E262 inline comment should start with '# '" elif token_type != tokenize.NL: prev_end = end @@ -829,7 +815,7 @@ line = logical_line if line.startswith('import '): found = line.find(',') - if -1 < found: + if -1 < found and ';' not in line[:found]: yield found, "E401 multiple imports on one line" @@ -857,10 +843,12 @@ E701: if foo == 'blah': one(); two(); three() E702: do_one(); do_two(); do_three() + E703: do_four(); # useless semicolon """ line = logical_line + last_char = len(line) - 1 found = line.find(':') - if -1 < found < len(line) - 1: + if -1 < found < last_char: before = line[:found] if (before.count('{') <= before.count('}') and # {'a': 1} (dict) before.count('[') <= before.count(']') and # [1:2] (slice) @@ -869,7 +857,10 @@ yield found, "E701 multiple statements on one line (colon)" found = line.find(';') if -1 < found: - yield found, "E702 multiple statements on one line (semicolon)" + if found < last_char: + yield found, "E702 multiple statements on one line (semicolon)" + else: + yield found, "E703 statement ends with a semicolon" def explicit_line_join(logical_line, tokens): @@ -954,16 +945,16 @@ """ match = COMPARE_TYPE_REGEX.search(logical_line) if match: - inst = match.group(3) + inst = match.group(1) if inst and isidentifier(inst) and inst not in SINGLETONS: return # Allow comparison for types which are not obvious - yield match.start(1), "E721 do not compare types, use 'isinstance()'" + yield match.start(0), "E721 do not compare types, use 'isinstance()'" def python_3000_has_key(logical_line): r""" - The {}.has_key() method will be removed in the future version of - Python. Use the 'in' operation instead. + The {}.has_key() method is removed in the Python 3. + Use the 'in' operation instead. Okay: if "alph" in d:\n print d["alph"] W601: assert d.has_key('alph') @@ -981,7 +972,7 @@ The paren-using form is preferred because when the exception arguments are long or include string formatting, you don't need to use line continuation characters thanks to the containing parentheses. The older - form will be removed in Python 3000. + form is removed in Python 3. Okay: raise DummyError("Message") W602: raise DummyError, "Message" @@ -995,7 +986,7 @@ """ != can also be written <>, but this is an obsolete usage kept for backwards compatibility only. New code should always use !=. - The older syntax is removed in Python 3000. + The older syntax is removed in Python 3. Okay: if a != 'no': W603: if a <> 'no': @@ -1007,7 +998,7 @@ def python_3000_backticks(logical_line): """ - Backticks are removed in Python 3000. + Backticks are removed in Python 3. Use repr() instead. Okay: val = repr(1 + 2) @@ -1051,8 +1042,11 @@ f.close() isidentifier = str.isidentifier - stdin_get_value = TextIOWrapper(sys.stdin.buffer, errors='ignore').read + + def stdin_get_value(): + return TextIOWrapper(sys.stdin.buffer, errors='ignore').read() readlines.__doc__ = " Read the source code." +noqa = re.compile(r'# no(?:qa|pep8)\b', re.I).search def expand_indent(line): @@ -1106,6 +1100,9 @@ def parse_udiff(diff, patterns=None, parent='.'): + """Return a dictionary of matching lines.""" + # For each file of the diff, the entry key is the filename, + # and the value is a set of row numbers to consider. rv = {} path = nrows = None for line in diff.splitlines(): @@ -1114,7 +1111,8 @@ nrows -= 1 continue if line[:3] == '@@ ': - row, nrows = [int(g) for g in HUNK_REGEX.match(line).groups()] + hunk_match = HUNK_REGEX.match(line) + row, nrows = [int(g or '1') for g in hunk_match.groups()] rv[path].update(range(row, row + nrows)) elif line[:3] == '+++': path = line[4:].split('\t', 1)[0] @@ -1141,18 +1139,38 @@ ############################################################################## -def find_checks(argument_name): +_checks = {'physical_line': {}, 'logical_line': {}, 'tree': {}} + + +def register_check(check, codes=None): """ - Find all globally visible functions where the first argument name - starts with argument_name. + Register a new check object. """ - for name, function in globals().items(): - if not inspect.isfunction(function): - continue - args = inspect.getargspec(function)[0] - if args and args[0].startswith(argument_name): - codes = ERRORCODE_REGEX.findall(function.__doc__ or '') - yield name, codes, function, args + def _add_check(check, kind, codes, args): + if check in _checks[kind]: + _checks[kind][check][0].extend(codes or []) + else: + _checks[kind][check] = (codes or [''], args) + if inspect.isfunction(check): + args = inspect.getargspec(check)[0] + if args and args[0] in ('physical_line', 'logical_line'): + if codes is None: + codes = ERRORCODE_REGEX.findall(check.__doc__ or '') + _add_check(check, args[0], codes, args) + elif inspect.isclass(check): + if inspect.getargspec(check.__init__)[0][:2] == ['self', 'tree']: + _add_check(check, 'tree', codes, None) + + +def init_checks_registry(): + """ + Register all globally visible functions where the first argument name + is 'physical_line' or 'logical_line'. + """ + mod = inspect.getmodule(register_check) + for (name, function) in inspect.getmembers(mod, inspect.isfunction): + register_check(function) +init_checks_registry() class Checker(object): @@ -1160,7 +1178,7 @@ Load a Python source file, tokenize it, check coding style. """ - def __init__(self, filename, lines=None, + def __init__(self, filename=None, lines=None, options=None, report=None, **kwargs): if options is None: options = StyleGuide(kwargs).options @@ -1169,12 +1187,16 @@ self._io_error = None self._physical_checks = options.physical_checks self._logical_checks = options.logical_checks + self._ast_checks = options.ast_checks self.max_line_length = options.max_line_length self.verbose = options.verbose self.filename = filename if filename is None: self.filename = 'stdin' self.lines = lines or [] + elif filename == '-': + self.filename = 'stdin' + self.lines = stdin_get_value().splitlines(True) elif lines is None: try: self.lines = readlines(filename) @@ -1187,6 +1209,16 @@ self.report = report or options.report self.report_error = self.report.error + def report_invalid_syntax(self): + exc_type, exc = sys.exc_info()[:2] + offset = exc.args[1] + if len(offset) > 2: + offset = offset[1:3] + self.report_error(offset[0], offset[1] or 0, + 'E901 %s: %s' % (exc_type.__name__, exc.args[0]), + self.report_invalid_syntax) + report_invalid_syntax.__doc__ = " Check if the syntax is valid." + def readline(self): """ Get the next line from the input buffer. @@ -1260,7 +1292,8 @@ length += len(text) previous = token self.logical_line = ''.join(logical) - assert self.logical_line.strip() == self.logical_line + # With Python 2, if the line ends with '\r\r\n' the assertion fails + # assert self.logical_line.strip() == self.logical_line def check_logical(self): """ @@ -1289,6 +1322,17 @@ self.report_error(orig_number, orig_offset, text, check) self.previous_logical = self.logical_line + def check_ast(self): + try: + tree = compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST) + except SyntaxError: + return self.report_invalid_syntax() + for name, cls, _ in self._ast_checks: + checker = cls(tree, self.filename) + for lineno, offset, text, check in checker.run(): + if not noqa(self.lines[lineno - 1]): + self.report_error(lineno, offset, text, check) + def generate_tokens(self): if self._io_error: self.report_error(1, 0, 'E902 %s' % self._io_error, readlines) @@ -1297,20 +1341,15 @@ for token in tokengen: yield token except (SyntaxError, tokenize.TokenError): - exc_type, exc = sys.exc_info()[:2] - offset = exc.args[1] - if len(offset) > 2: - offset = offset[1:3] - self.report_error(offset[0], offset[1], - 'E901 %s: %s' % (exc_type.__name__, exc.args[0]), - self.generate_tokens) - generate_tokens.__doc__ = " Check if the syntax is valid." + self.report_invalid_syntax() def check_all(self, expected=None, line_offset=0): """ Run all checks on the input file. """ self.report.init_file(self.filename, self.lines, expected, line_offset) + if self._ast_checks: + self.check_ast() self.line_number = 0 self.indent_char = None self.indent_level = 0 @@ -1446,6 +1485,7 @@ class FileReport(BaseReport): + """Collect the results of the checks and print only the filenames.""" print_filename = True @@ -1460,17 +1500,29 @@ self._show_source = options.show_source self._show_pep8 = options.show_pep8 + def init_file(self, filename, lines, expected, line_offset): + """Signal a new file.""" + self._deferred_print = [] + return super(StandardReport, self).init_file( + filename, lines, expected, line_offset) + def error(self, line_number, offset, text, check): - """ - Report an error, according to options. - """ + """Report an error, according to options.""" code = super(StandardReport, self).error(line_number, offset, text, check) if code and (self.counters[code] == 1 or self._repeat): + self._deferred_print.append( + (line_number, offset, code, text[5:], check.__doc__)) + return code + + def get_file_results(self): + """Print the result and return the overall count for this file.""" + self._deferred_print.sort() + for line_number, offset, code, text, doc in self._deferred_print: print(self._fmt % { 'path': self.filename, 'row': self.line_offset + line_number, 'col': offset + 1, - 'code': code, 'text': text[5:], + 'code': code, 'text': text, }) if self._show_source: if line_number > len(self.lines): @@ -1479,9 +1531,9 @@ line = self.lines[line_number - 1] print(line.rstrip()) print(' ' * offset + '^') - if self._show_pep8: - print(check.__doc__.lstrip('\n').rstrip()) - return code + if self._show_pep8 and doc: + print(doc.lstrip('\n').rstrip()) + return self.file_errors class DiffReport(StandardReport): @@ -1497,54 +1549,17 @@ return super(DiffReport, self).error(line_number, offset, text, check) -class TestReport(StandardReport): - """Collect the results for the tests.""" - - def __init__(self, options): - options.benchmark_keys += ['test cases', 'failed tests'] - super(TestReport, self).__init__(options) - self._verbose = options.verbose - - def get_file_results(self): - # Check if the expected errors were found - label = '%s:%s:1' % (self.filename, self.line_offset) - codes = sorted(self.expected) - for code in codes: - if not self.counters.get(code): - self.file_errors += 1 - self.total_errors += 1 - print('%s: error %s not found' % (label, code)) - if self._verbose and not self.file_errors: - print('%s: passed (%s)' % - (label, ' '.join(codes) or 'Okay')) - self.counters['test cases'] += 1 - if self.file_errors: - self.counters['failed tests'] += 1 - # Reset counters - for key in set(self.counters) - set(self._benchmark_keys): - del self.counters[key] - self.messages = {} - return self.file_errors - - def print_results(self): - results = ("%(physical lines)d lines tested: %(files)d files, " - "%(test cases)d test cases%%s." % self.counters) - if self.total_errors: - print(results % ", %s failures" % self.total_errors) - else: - print(results % "") - print("Test failed." if self.total_errors else "Test passed.") - - class StyleGuide(object): """Initialize a PEP-8 instance with few options.""" def __init__(self, *args, **kwargs): # build options from the command line + self.checker_class = kwargs.pop('checker_class', Checker) parse_argv = kwargs.pop('parse_argv', False) config_file = kwargs.pop('config_file', None) - options, self.paths = process_options(parse_argv=parse_argv, - config_file=config_file) + parser = kwargs.pop('parser', None) + options, self.paths = process_options( + parse_argv=parse_argv, config_file=config_file, parser=parser) if args or kwargs: # build options from dict options_dict = dict(*args, **kwargs) @@ -1567,6 +1582,7 @@ options.ignore_code = self.ignore_code options.physical_checks = self.get_checks('physical_line') options.logical_checks = self.get_checks('logical_line') + options.ast_checks = self.get_checks('tree') self.init_report() def init_report(self, reporter=None): @@ -1581,11 +1597,14 @@ report = self.options.report runner = self.runner report.start() - for path in paths: - if os.path.isdir(path): - self.input_dir(path) - elif not self.excluded(path): - runner(path) + try: + for path in paths: + if os.path.isdir(path): + self.input_dir(path) + elif not self.excluded(path): + runner(path) + except KeyboardInterrupt: + print('... stopped') report.stop() return report @@ -1593,7 +1612,8 @@ """Run all checks on a Python source file.""" if self.options.verbose: print('checking %s' % filename) - fchecker = Checker(filename, lines=lines, options=self.options) + fchecker = self.checker_class( + filename, lines=lines, options=self.options) return fchecker.check_all(expected=expected, line_offset=line_offset) def input_dir(self, dirname): @@ -1610,7 +1630,7 @@ print('directory ' + root) counters['directories'] += 1 for subdir in sorted(dirs): - if self.excluded(subdir): + if self.excluded(os.path.join(root, subdir)): dirs.remove(subdir) for filename in sorted(files): # contain a pattern that matches? @@ -1623,7 +1643,10 @@ Check if options.exclude contains a pattern that matches filename. """ basename = os.path.basename(filename) - return filename_match(basename, self.options.exclude, default=False) + return any((filename_match(filename, self.options.exclude, + default=False), + filename_match(basename, self.options.exclude, + default=False))) def ignore_code(self, code): """ @@ -1642,168 +1665,15 @@ starts with argument_name and which contain selected tests. """ checks = [] - for name, codes, function, args in find_checks(argument_name): + for check, attrs in _checks[argument_name].items(): + (codes, args) = attrs if any(not (code and self.ignore_code(code)) for code in codes): - checks.append((name, function, args)) + checks.append((check.__name__, check, args)) return sorted(checks) -def init_tests(pep8style): - """ - Initialize testing framework. - - A test file can provide many tests. Each test starts with a - declaration. This declaration is a single line starting with '#:'. - It declares codes of expected failures, separated by spaces or 'Okay' - if no failure is expected. - If the file does not contain such declaration, it should pass all - tests. If the declaration is empty, following lines are not checked, - until next declaration. - - Examples: - - * Only E224 and W701 are expected: #: E224 W701 - * Following example is conform: #: Okay - * Don't check these lines: #: - """ - report = pep8style.init_report(TestReport) - runner = pep8style.input_file - - def run_tests(filename): - """Run all the tests from a file.""" - lines = readlines(filename) + ['#:\n'] - line_offset = 0 - codes = ['Okay'] - testcase = [] - count_files = report.counters['files'] - for index, line in enumerate(lines): - if not line.startswith('#:'): - if codes: - # Collect the lines of the test case - testcase.append(line) - continue - if codes and index: - codes = [c for c in codes if c != 'Okay'] - # Run the checker - runner(filename, testcase, expected=codes, - line_offset=line_offset) - # output the real line numbers - line_offset = index + 1 - # configure the expected errors - codes = line.split()[1:] - # empty the test case buffer - del testcase[:] - report.counters['files'] = count_files + 1 - return report.counters['failed tests'] - - pep8style.runner = run_tests - - -def selftest(options): - """ - Test all check functions with test cases in docstrings. - """ - count_failed = count_all = 0 - report = BaseReport(options) - counters = report.counters - checks = options.physical_checks + options.logical_checks - for name, check, argument_names in checks: - for line in check.__doc__.splitlines(): - line = line.lstrip() - match = SELFTEST_REGEX.match(line) - if match is None: - continue - code, source = match.groups() - checker = Checker(None, options=options, report=report) - for part in source.split(r'\n'): - part = part.replace(r'\t', '\t') - part = part.replace(r'\s', ' ') - checker.lines.append(part + '\n') - checker.check_all() - error = None - if code == 'Okay': - if len(counters) > len(options.benchmark_keys): - codes = [key for key in counters - if key not in options.benchmark_keys] - error = "incorrectly found %s" % ', '.join(codes) - elif not counters.get(code): - error = "failed to find %s" % code - # Keep showing errors for multiple tests - for key in set(counters) - set(options.benchmark_keys): - del counters[key] - report.messages = {} - count_all += 1 - if not error: - if options.verbose: - print("%s: %s" % (code, source)) - else: - count_failed += 1 - print("%s: %s:" % (__file__, error)) - for line in checker.lines: - print(line.rstrip()) - return count_failed, count_all - - -def read_config(options, args, arglist, parser): - """Read both user configuration and local configuration.""" - config = RawConfigParser() - - user_conf = options.config - if user_conf and os.path.isfile(user_conf): - if options.verbose: - print('user configuration: %s' % user_conf) - config.read(user_conf) - - parent = tail = args and os.path.abspath(os.path.commonprefix(args)) - while tail: - local_conf = os.path.join(parent, '.pep8') - if os.path.isfile(local_conf): - if options.verbose: - print('local configuration: %s' % local_conf) - config.read(local_conf) - break - parent, tail = os.path.split(parent) - - if config.has_section('pep8'): - option_list = dict([(o.dest, o.type or o.action) - for o in parser.option_list]) - - # First, read the default values - new_options, _ = parser.parse_args([]) - - # Second, parse the configuration - for opt in config.options('pep8'): - if options.verbose > 1: - print(' %s = %s' % (opt, config.get('pep8', opt))) - if opt.replace('_', '-') not in parser.config_options: - print('Unknown option: \'%s\'\n not in [%s]' % - (opt, ' '.join(parser.config_options))) - sys.exit(1) - normalized_opt = opt.replace('-', '_') - opt_type = option_list[normalized_opt] - if opt_type in ('int', 'count'): - value = config.getint('pep8', opt) - elif opt_type == 'string': - value = config.get('pep8', opt) - else: - assert opt_type in ('store_true', 'store_false') - value = config.getboolean('pep8', opt) - setattr(new_options, normalized_opt, value) - - # Third, overwrite with the command-line options - options, _ = parser.parse_args(arglist, values=new_options) - - return options - - -def process_options(arglist=None, parse_argv=False, config_file=None): - """Process options passed either via arglist or via command line args.""" - if not arglist and not parse_argv: - # Don't read the command line if the module is used as a library. - arglist = [] - if config_file is True: - config_file = DEFAULT_CONFIG - parser = OptionParser(version=__version__, +def get_parser(prog='pep8', version=__version__): + parser = OptionParser(prog=prog, version=version, usage="%prog [options] input ...") parser.config_options = [ 'exclude', 'filename', 'select', 'ignore', 'max-line-length', 'count', @@ -1848,30 +1718,110 @@ help="report only lines changed according to the " "unified diff received on STDIN") group = parser.add_option_group("Testing Options") - group.add_option('--testsuite', metavar='dir', - help="run regression tests from dir") - group.add_option('--doctest', action='store_true', - help="run doctest on myself") + if os.path.exists(TESTSUITE_PATH): + group.add_option('--testsuite', metavar='dir', + help="run regression tests from dir") + group.add_option('--doctest', action='store_true', + help="run doctest on myself") group.add_option('--benchmark', action='store_true', help="measure processing speed") - group = parser.add_option_group("Configuration", description=( - "The project options are read from the [pep8] section of the .pep8 " - "file located in any parent folder of the path(s) being processed. " - "Allowed options are: %s." % ', '.join(parser.config_options))) - group.add_option('--config', metavar='path', default=config_file, - help="config file location (default: %default)") + return parser + + +def read_config(options, args, arglist, parser): + """Read both user configuration and local configuration.""" + config = RawConfigParser() + user_conf = options.config + if user_conf and os.path.isfile(user_conf): + if options.verbose: + print('user configuration: %s' % user_conf) + config.read(user_conf) + + parent = tail = args and os.path.abspath(os.path.commonprefix(args)) + while tail: + for name in PROJECT_CONFIG: + local_conf = os.path.join(parent, name) + if os.path.isfile(local_conf): + break + else: + parent, tail = os.path.split(parent) + continue + if options.verbose: + print('local configuration: %s' % local_conf) + config.read(local_conf) + break + + pep8_section = parser.prog + if config.has_section(pep8_section): + option_list = dict([(o.dest, o.type or o.action) + for o in parser.option_list]) + + # First, read the default values + new_options, _ = parser.parse_args([]) + + # Second, parse the configuration + for opt in config.options(pep8_section): + if options.verbose > 1: + print(" %s = %s" % (opt, config.get(pep8_section, opt))) + if opt.replace('_', '-') not in parser.config_options: + print("Unknown option: '%s'\n not in [%s]" % + (opt, ' '.join(parser.config_options))) + sys.exit(1) + normalized_opt = opt.replace('-', '_') + opt_type = option_list[normalized_opt] + if opt_type in ('int', 'count'): + value = config.getint(pep8_section, opt) + elif opt_type == 'string': + value = config.get(pep8_section, opt) + else: + assert opt_type in ('store_true', 'store_false') + value = config.getboolean(pep8_section, opt) + setattr(new_options, normalized_opt, value) + + # Third, overwrite with the command-line options + options, _ = parser.parse_args(arglist, values=new_options) + options.doctest = options.testsuite = False + return options + + +def process_options(arglist=None, parse_argv=False, config_file=None, + parser=None): + """Process options passed either via arglist or via command line args.""" + if not arglist and not parse_argv: + # Don't read the command line if the module is used as a library. + arglist = [] + if not parser: + parser = get_parser() + if not parser.has_option('--config'): + if config_file is True: + config_file = DEFAULT_CONFIG + group = parser.add_option_group("Configuration", description=( + "The project options are read from the [%s] section of the " + "tox.ini file or the setup.cfg file located in any parent folder " + "of the path(s) being processed. Allowed options are: %s." % + (parser.prog, ', '.join(parser.config_options)))) + group.add_option('--config', metavar='path', default=config_file, + help="user config file location (default: %default)") options, args = parser.parse_args(arglist) options.reporter = None - if options.testsuite: + if options.ensure_value('testsuite', False): args.append(options.testsuite) - elif not options.doctest: + elif not options.ensure_value('doctest', False): if parse_argv and not args: - if os.path.exists('.pep8') or options.diff: + if options.diff: args = ['.'] else: - parser.error('input not specified') + import select + # wait for 1 second on the stdin fd + reads, __, __ = select.select([sys.stdin], [], [], 1.) + if reads: + args = ['-'] + elif any(os.path.exists(name) for name in PROJECT_CONFIG): + args = ['.'] + else: + parser.error('input not specified') options = read_config(options, args, arglist, parser) options.reporter = parse_argv and options.quiet == 1 and FileReport @@ -1901,20 +1851,13 @@ """Parse options and run checks on Python source.""" pep8style = StyleGuide(parse_argv=True, config_file=True) options = pep8style.options - if options.doctest: - import doctest - fail_d, done_d = doctest.testmod(report=False, verbose=options.verbose) - fail_s, done_s = selftest(options) - count_failed = fail_s + fail_d - if not options.quiet: - count_passed = done_d + done_s - count_failed - print("%d passed and %d failed." % (count_passed, count_failed)) - print("Test failed." if count_failed else "Test passed.") - if count_failed: - sys.exit(1) - if options.testsuite: - init_tests(pep8style) - report = pep8style.check_files() + if options.doctest or options.testsuite: + sys.path[:0] = [TESTSUITE_PATH] + from test_pep8 import run_tests + del sys.path[0] + report = run_tests(pep8style, options.doctest, options.testsuite) + else: + report = pep8style.check_files() if options.statistics: report.print_statistics() if options.benchmark: @@ -1926,6 +1869,5 @@ sys.stderr.write(str(report.total_errors) + '\n') sys.exit(1) - if __name__ == '__main__': _main() diff -Nru pep8-1.3.3/setup.py pep8-1.4.4/setup.py --- pep8-1.3.3/setup.py 2012-06-13 21:41:02.000000000 +0000 +++ pep8-1.4.4/setup.py 2013-02-09 21:25:59.000000000 +0000 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import with_statement -from setuptools import setup, find_packages +from setuptools import setup def get_version(): @@ -12,7 +12,7 @@ def get_long_description(): descr = [] - for fname in 'README.rst', 'CHANGES.txt': # , 'TODO.txt' + for fname in 'README.rst', 'CHANGES.txt': with open(fname) as f: descr.append(f.read()) return '\n\n'.join(descr) @@ -26,7 +26,7 @@ keywords='pep8', author='Johann C. Rocholl', author_email='johann@rocholl.net', - url='http://github.com/jcrocholl/pep8', + url='http://pep8.readthedocs.org/', license='Expat license', py_modules=['pep8'], namespace_packages=[], diff -Nru pep8-1.3.3/testsuite/E12not.py pep8-1.4.4/testsuite/E12not.py --- pep8-1.3.3/testsuite/E12not.py 2012-06-27 06:34:27.000000000 +0000 +++ pep8-1.4.4/testsuite/E12not.py 2013-02-14 19:03:13.000000000 +0000 @@ -530,4 +530,19 @@ If you would like to see debugging output, try: %s -d5 ''' % sys.argv[0]) + + +d = { # comment + 1: 2 +} + +# issue 138 +[ + 12, # this is a multi-line inline + # comment +] +# issue 151 +if a > b and \ + c > d: + moo_like_a_cow() # diff -Nru pep8-1.3.3/testsuite/E21.py pep8-1.4.4/testsuite/E21.py --- pep8-1.3.3/testsuite/E21.py 2012-04-06 15:36:29.000000000 +0000 +++ pep8-1.4.4/testsuite/E21.py 2012-12-19 00:48:14.000000000 +0000 @@ -4,9 +4,6 @@ dict ['key'] = list [index] #: E211 dict['key'] ['subkey'] = list[index] -#: E225 -def squares(n): - return (i**2 for i in range(n)) #: Okay spam(1) dict['key'] = list[index] diff -Nru pep8-1.3.3/testsuite/E22.py pep8-1.4.4/testsuite/E22.py --- pep8-1.3.3/testsuite/E22.py 2012-06-03 22:24:38.000000000 +0000 +++ pep8-1.4.4/testsuite/E22.py 2013-02-24 11:59:57.000000000 +0000 @@ -47,37 +47,85 @@ #: E225 -i=i+1 -#: E225 submitted +=1 #: E225 -x = x*2 - 1 -#: E225 -hypot2 = x*x + y*y +submitted+= 1 #: E225 -c = (a+b) * (a-b) +c =-1 #: E225 -c = (a + b)*(a - b) +x = x /2 - 1 #: E225 -c = (a +b)*(a - b) +c = alpha -4 #: E225 -c =-1 +c = alpha- 4 #: E225 -c = alpha -4 +z = x **y #: E225 z = (x + 1) **y #: E225 -norman = True+False +z = (x + 1)** y #: E225 -_1MB = 2 ** 20 _1kB = _1MB >>10 +#: E225 +_1kB = _1MB>> 10 +#: E225 +i=i+ 1 +#: E225 +i=i +1 +#: E225 E226 +i=i+1 +#: E225 E226 +i =i+1 +#: E225 E226 +i= i+1 +#: E225 E226 +c = (a +b)*(a - b) +#: E225 E226 +c = (a+ b)*(a - b) #: + +#: E226 +z = 2**30 +#: E226 +c = (a+b) * (a-b) +#: E226 +norman = True+False +#: E226 +x = x*2 - 1 +#: E226 +x = x/2 - 1 +#: E226 +hypot2 = x*x + y*y +#: E226 +c = (a + b)*(a - b) +#: E226 +def squares(n): + return (i**2 for i in range(n)) +#: E227 +_1kB = _1MB>>10 +#: E227 +_1MB = _1kB<<10 +#: E227 +a = b|c +#: E227 +b = c&a +#: E227 +c = b^a +#: E228 +a = b%c +#: E228 +msg = fmt%(errno, errmsg) +#: E228 +msg = "Error %d occured"%errno +#: + #: Okay i = i + 1 submitted += 1 x = x * 2 - 1 hypot2 = x * x + y * y c = (a + b) * (a - b) +_1MB = 2 ** 20 foo(bar, key='word', *args, **kwargs) baz(**kwargs) negative = -1 @@ -88,7 +136,13 @@ if not -5 < x < +5: print >>sys.stderr, "x is out of range." print >> sys.stdout, "x is an integer." +z = 2 ** 30 +x = x / 2 - 1 -if True: +if alpha[:-i]: *a, b = (1, 2, 3) + + +def squares(n): + return (i ** 2 for i in range(n)) #: diff -Nru pep8-1.3.3/testsuite/E23.py pep8-1.4.4/testsuite/E23.py --- pep8-1.3.3/testsuite/E23.py 2012-05-29 11:42:00.000000000 +0000 +++ pep8-1.4.4/testsuite/E23.py 2012-12-09 19:23:31.000000000 +0000 @@ -2,9 +2,12 @@ a = (1,2) #: E231 a[b1,:] +#: E231 +a = [{'a':''}] #: Okay a = (4,) b = (5, ) +c = {'text': text[5:]} result = { 'key1': 'value', diff -Nru pep8-1.3.3/testsuite/E24.py pep8-1.4.4/testsuite/E24.py --- pep8-1.3.3/testsuite/E24.py 2012-06-02 21:31:33.000000000 +0000 +++ pep8-1.4.4/testsuite/E24.py 2012-12-09 21:35:11.000000000 +0000 @@ -6,3 +6,8 @@ a = (1, 2) # tab before 2 #: Okay b = (1, 20) # space before 20 +#: E241 +# issue 135 +more_spaces = [a, b, + ef, +h, + c, -d] diff -Nru pep8-1.3.3/testsuite/E26.py pep8-1.4.4/testsuite/E26.py --- pep8-1.3.3/testsuite/E26.py 2012-04-06 15:36:29.000000000 +0000 +++ pep8-1.4.4/testsuite/E26.py 2012-12-25 23:51:24.000000000 +0000 @@ -4,3 +4,10 @@ x = x + 1 #Increment x #: E262 x = x + 1 # Increment x +#: E262 +x = y + 1 #: Increment x +#: Okay +pass # an inline comment +x = x + 1 # Increment x +y = y + 1 #: Increment x +#: diff -Nru pep8-1.3.3/testsuite/E27.py pep8-1.4.4/testsuite/E27.py --- pep8-1.3.3/testsuite/E27.py 2012-06-01 17:19:37.000000000 +0000 +++ pep8-1.4.4/testsuite/E27.py 2012-12-09 19:33:21.000000000 +0000 @@ -4,6 +4,8 @@ True and False #: E272 True and False +#: E271 +if 1: #: E273 True and False #: E273 E274 diff -Nru pep8-1.3.3/testsuite/E70.py pep8-1.4.4/testsuite/E70.py --- pep8-1.3.3/testsuite/E70.py 2012-04-06 15:36:29.000000000 +0000 +++ pep8-1.4.4/testsuite/E70.py 2013-01-18 21:46:55.000000000 +0000 @@ -2,3 +2,8 @@ if a: a = False #: E702 a = False; b = True +#: E702 +import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe) +#: E703 +import shlex; +#: diff -Nru pep8-1.3.3/testsuite/E72.py pep8-1.4.4/testsuite/E72.py --- pep8-1.3.3/testsuite/E72.py 2012-05-27 20:29:13.000000000 +0000 +++ pep8-1.4.4/testsuite/E72.py 2013-02-24 13:47:07.000000000 +0000 @@ -14,6 +14,30 @@ if type(res) is not types.ListType: pass +#: E721 +assert type(res) == type(False) or type(res) == type(None) +#: E721 +assert type(res) == type([]) +#: E721 +assert type(res) == type(()) +#: E721 +assert type(res) == type((0,)) +#: E721 +assert type(res) == type((0)) +#: E721 +assert type(res) != type((1, )) +#: E721 +assert type(res) is type((1, )) +#: E721 +assert type(res) is not type((1, )) +#: E211 E721 +assert type(res) == type ([2, ]) +#: E201 E202 E721 +assert type(res) == type( ( ) ) +#: E201 E202 E721 +assert type(res) == type( (0, ) ) +#: + #: Okay import types diff -Nru pep8-1.3.3/testsuite/E90.py pep8-1.4.4/testsuite/E90.py --- pep8-1.3.3/testsuite/E90.py 2012-06-15 18:16:21.000000000 +0000 +++ pep8-1.4.4/testsuite/E90.py 2013-02-24 14:59:55.000000000 +0000 @@ -8,7 +8,7 @@ pass except: print 'Whoops' -#: E122 E128 E225 E251 E701 +#: E122 E225 E251 E701 # Do not crash if code is invalid if msg: @@ -16,4 +16,11 @@ def lasting(self, duration=300): progress = self._progress.setdefault('foo', {} +#: Okay + +# Issue #119 +# Do not crash with Python2 if the line endswith '\r\r\n' +EMPTY_SET = set() +SET_TYPE = type(EMPTY_SET) +toto = 0 + 0 #: diff -Nru pep8-1.3.3/testsuite/W19.py pep8-1.4.4/testsuite/W19.py --- pep8-1.3.3/testsuite/W19.py 2012-04-06 15:36:29.000000000 +0000 +++ pep8-1.4.4/testsuite/W19.py 2013-02-24 15:14:29.000000000 +0000 @@ -1,3 +1,104 @@ #: W191 if False: print # indented with 1 tab +#: + + +#: E126 W191 +y = x == 2 \ + or x == 3 +#: E101 W191 +if ( + x == ( + 3 + ) or + y == 4): + pass +#: E101 W191 +if x == 2 \ + or y > 1 \ + or x == 3: + pass +#: E101 W191 +if x == 2 \ + or y > 1 \ + or x == 3: + pass +#: + +#: E101 W191 +if (foo == bar and + baz == frop): + pass +#: E101 W191 +if ( + foo == bar and + baz == frop +): + pass +#: + +#: E101 W191 +if start[1] > end_col and not ( + over_indent == 4 and indent_next): + return(0, "E121 continuation line over-" + "indented for visual indent") +#: + +#: E101 W191 + + +def long_function_name( + var_one, var_two, var_three, + var_four): + print(var_one) +#: E101 W191 +if ((row < 0 or self.moduleCount <= row or + col < 0 or self.moduleCount <= col)): + raise Exception("%s,%s - %s" % (row, col, self.moduleCount)) +#: E101 W191 +if bar: + return( + start, 'E121 lines starting with a ' + 'closing bracket should be indented ' + "to match that of the opening " + "bracket's line" + ) +# +#: E101 W191 +# you want vertical alignment, so use a parens +if ((foo.bar("baz") and + foo.bar("frop") + )): + print "yes" +#: E101 W191 +# also ok, but starting to look like LISP +if ((foo.bar("baz") and + foo.bar("frop"))): + print "yes" +#: E101 W191 +if (a == 2 or + b == "abc def ghi" + "jkl mno"): + return True +#: E101 W191 +if (a == 2 or + b == """abc def ghi +jkl mno"""): + return True +#: E101 W191 +if length > options.max_line_length: + return options.max_line_length, \ + "E501 line too long (%d characters)" % length + + +# +#: E101 W191 +if os.path.exists(os.path.join(path, PEP8_BIN)): + cmd = ([os.path.join(path, PEP8_BIN)] + + self._pep8_options(targetfile)) +#: E101 W191 +if foo is None and bar is "frop" and \ + blah == 'yeah': + blah = 'yeahnah' +#: diff -Nru pep8-1.3.3/testsuite/W60.py pep8-1.4.4/testsuite/W60.py --- pep8-1.3.3/testsuite/W60.py 2012-04-06 15:36:29.000000000 +0000 +++ pep8-1.4.4/testsuite/W60.py 2012-12-22 09:47:09.000000000 +0000 @@ -3,6 +3,8 @@ print a #: W602 raise DummyError, "Message" +#: W602 +raise ValueError, "hello %s %s" % (1, 2) #: Okay raise type_, val, tb #: W603 diff -Nru pep8-1.3.3/testsuite/test_pep8.py pep8-1.4.4/testsuite/test_pep8.py --- pep8-1.3.3/testsuite/test_pep8.py 1970-01-01 00:00:00.000000000 +0000 +++ pep8-1.4.4/testsuite/test_pep8.py 2013-02-24 11:22:13.000000000 +0000 @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os.path +import re +import sys + +import pep8 +from pep8 import Checker, StyleGuide, BaseReport, StandardReport, readlines + +SELFTEST_REGEX = re.compile(r'\b(Okay|[EW]\d{3}):\s(.*)') + + +class TestReport(StandardReport): + """Collect the results for the tests.""" + + def __init__(self, options): + options.benchmark_keys += ['test cases', 'failed tests'] + super(TestReport, self).__init__(options) + self._verbose = options.verbose + + def get_file_results(self): + # Check if the expected errors were found + label = '%s:%s:1' % (self.filename, self.line_offset) + codes = sorted(self.expected) + for code in codes: + if not self.counters.get(code): + self.file_errors += 1 + self.total_errors += 1 + print('%s: error %s not found' % (label, code)) + if self._verbose and not self.file_errors: + print('%s: passed (%s)' % + (label, ' '.join(codes) or 'Okay')) + self.counters['test cases'] += 1 + if self.file_errors: + self.counters['failed tests'] += 1 + # Reset counters + for key in set(self.counters) - set(self._benchmark_keys): + del self.counters[key] + self.messages = {} + return super(TestReport, self).get_file_results() + + def print_results(self): + results = ("%(physical lines)d lines tested: %(files)d files, " + "%(test cases)d test cases%%s." % self.counters) + if self.total_errors: + print(results % ", %s failures" % self.total_errors) + else: + print(results % "") + print("Test failed." if self.total_errors else "Test passed.") + + +def init_tests(pep8style): + """ + Initialize testing framework. + + A test file can provide many tests. Each test starts with a + declaration. This declaration is a single line starting with '#:'. + It declares codes of expected failures, separated by spaces or 'Okay' + if no failure is expected. + If the file does not contain such declaration, it should pass all + tests. If the declaration is empty, following lines are not checked, + until next declaration. + + Examples: + + * Only E224 and W701 are expected: #: E224 W701 + * Following example is conform: #: Okay + * Don't check these lines: #: + """ + report = pep8style.init_report(TestReport) + runner = pep8style.input_file + + def run_tests(filename): + """Run all the tests from a file.""" + lines = readlines(filename) + ['#:\n'] + line_offset = 0 + codes = ['Okay'] + testcase = [] + count_files = report.counters['files'] + for index, line in enumerate(lines): + if not line.startswith('#:'): + if codes: + # Collect the lines of the test case + testcase.append(line) + continue + if codes and index: + codes = [c for c in codes if c != 'Okay'] + # Run the checker + runner(filename, testcase, expected=codes, + line_offset=line_offset) + # output the real line numbers + line_offset = index + 1 + # configure the expected errors + codes = line.split()[1:] + # empty the test case buffer + del testcase[:] + report.counters['files'] = count_files + 1 + return report.counters['failed tests'] + + pep8style.runner = run_tests + + +def selftest(options): + """ + Test all check functions with test cases in docstrings. + """ + count_failed = count_all = 0 + report = BaseReport(options) + counters = report.counters + checks = options.physical_checks + options.logical_checks + for name, check, argument_names in checks: + for line in check.__doc__.splitlines(): + line = line.lstrip() + match = SELFTEST_REGEX.match(line) + if match is None: + continue + code, source = match.groups() + lines = [part.replace(r'\t', '\t') + '\n' + for part in source.split(r'\n')] + checker = Checker(lines=lines, options=options, report=report) + checker.check_all() + error = None + if code == 'Okay': + if len(counters) > len(options.benchmark_keys): + codes = [key for key in counters + if key not in options.benchmark_keys] + error = "incorrectly found %s" % ', '.join(codes) + elif not counters.get(code): + error = "failed to find %s" % code + # Keep showing errors for multiple tests + for key in set(counters) - set(options.benchmark_keys): + del counters[key] + report.messages = {} + count_all += 1 + if not error: + if options.verbose: + print("%s: %s" % (code, source)) + else: + count_failed += 1 + print("%s: %s:" % (pep8.__file__, error)) + for line in checker.lines: + print(line.rstrip()) + return count_failed, count_all + + +def run_tests(style, doctest=True, testsuite=False): + options = style.options + if doctest: + import doctest + fail_d, done_d = doctest.testmod(report=False, verbose=options.verbose) + fail_s, done_s = selftest(options) + count_failed = fail_s + fail_d + if not options.quiet: + count_passed = done_d + done_s - count_failed + print("%d passed and %d failed." % (count_passed, count_failed)) + print("Test failed." if count_failed else "Test passed.") + if count_failed: + sys.exit(1) + if testsuite: + init_tests(style) + return style.check_files() + + +if __name__ == '__main__': + if len(sys.argv) > 1: + sys.exit(pep8._main()) + pep8style = StyleGuide(paths=[os.path.dirname(__file__)], ignore=None) + report = run_tests(pep8style, doctest=True, testsuite=True) + report.print_results() + sys.exit(1 if report.total_errors else 0)