diff --git a/.travis.yml b/.travis.yml index f633fe4c..02e096e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,18 @@ language: python matrix: include: + - python: 2.7 + env: TOXENV=py27 + - python: 3.4 + env: TOXENV=py34 - python: 3.5 env: TOXENV=py35 -env: - - TOXENV=py26 - - TOXENV=py27 - - TOXENV=py33 - - TOXENV=py34 - - TOXENV=pypy + - python: 3.6 + env: TOXENV=py36 + - python: pypy + env: TOXENV=pypy + - python: pypy3 + env: TOXENV=pypy3 install: - travis_retry pip install coveralls tox script: diff --git a/README.rst b/README.rst index 78639153..be9b1c1f 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ diff-cover |build-status| |coverage-status| |docs-status| ========================================================= Automatically find diff lines that need test coverage. -Also finds diff lines that have violations (according to tools such as pep8, +Also finds diff lines that have violations (according to tools such as pycodestyle, pyflakes, flake8, or pylint). This is used as a code quality metric during code reviews. @@ -119,7 +119,7 @@ You can use diff-cover to see quality reports on the diff as well by running diff-quality --violations= -Where ``tool`` is the quality checker to use. Currently ``pep8``, ``pyflakes``, +Where ``tool`` is the quality checker to use. Currently ``pycodestyle``, ``pyflakes``, ``flake8``, and ``pylint`` are supported, but more checkers can (and should!) be integrated. @@ -129,9 +129,9 @@ Like ``diff-cover``, HTML reports can be generated with diff-quality --violations= --html-report report.html -If you have already generated a report using ``pep8``, ``pyflakes``, ``flake8``, +If you have already generated a report using ``pycodestyle``, ``pyflakes``, ``flake8``, or ``pylint`` you can pass the report to ``diff-quality``. This is more -efficient than letting ``diff-quality`` re-run ``pep8``, ``pyflakes``, +efficient than letting ``diff-quality`` re-run ``pycodestyle``, ``pyflakes``, ``flake8``, or ``pylint``. .. code:: bash @@ -145,15 +145,15 @@ efficient than letting ``diff-quality`` re-run ``pep8``, ``pyflakes``, # Use the generated pylint report when running diff-quality diff-quality --violations=pylint pylint_report.txt - # Use a generated pep8 report when running diff-quality. - pep8 > pep8_report.txt - diff-quality --violations=pep8 pep8_report.txt + # Use a generated pycodestyle report when running diff-quality. + pycodestyle > pycodestyle_report.txt + diff-quality --violations=pycodestyle pycodestyle_report.txt Note that you must use the ``-f parseable`` option to generate the ``pylint`` report for pylint versions less than 1.0 and the ``--msg-template`` option for versions >= 1.0. -``diff-quality`` will also accept multiple ``pep8``, ``pyflakes``, ``flake8``, +``diff-quality`` will also accept multiple ``pycodestyle``, ``pyflakes``, ``flake8``, or ``pylint`` reports: .. code:: bash @@ -164,7 +164,7 @@ If you need to pass in additional options you can with the ``options`` flag .. code:: bash - diff-quality --violations=pep8 --options="--exclude='*/migrations*' --statistics" pep8_report.txt + diff-quality --violations=pycodestyle --options="--exclude='*/migrations*' --statistics" pycodestyle_report.txt Compare Branch -------------- @@ -184,7 +184,7 @@ below a certain threshold specify the fail-under parameter .. code:: bash diff-cover coverage.xml --fail-under=80 - diff-quality --violations=pep8 --fail-under=80 + diff-quality --violations=pycodestyle --fail-under=80 The above will return a non zero status if the coverage or quality score was below 80% @@ -263,7 +263,7 @@ For example, setting up python 3: pyvenv venv source venv/bin/activate - pip install -r test-requirements-py27-py3.txt + pip install -r test-requirements.txt Special Thanks diff --git a/diff_cover/tests/fixtures/empty_pep8_violations.txt b/diff_cover/tests/fixtures/empty_pycodestyle_violations.txt similarity index 88% rename from diff_cover/tests/fixtures/empty_pep8_violations.txt rename to diff_cover/tests/fixtures/empty_pycodestyle_violations.txt index be36898e..8f266081 100644 --- a/diff_cover/tests/fixtures/empty_pep8_violations.txt +++ b/diff_cover/tests/fixtures/empty_pycodestyle_violations.txt @@ -1,6 +1,6 @@ ------------- Diff Quality -Quality Report: pep8 +Quality Report: pycodestyle Diff: origin/master...HEAD, staged and unstaged changes ------------- violations_test_file.py (100%) diff --git a/diff_cover/tests/fixtures/pep8_report.txt b/diff_cover/tests/fixtures/pycodestyle_report.txt similarity index 100% rename from diff_cover/tests/fixtures/pep8_report.txt rename to diff_cover/tests/fixtures/pycodestyle_report.txt diff --git a/diff_cover/tests/fixtures/pep8_violations_report.html b/diff_cover/tests/fixtures/pycodestyle_violations_report.html similarity index 99% rename from diff_cover/tests/fixtures/pep8_violations_report.html rename to diff_cover/tests/fixtures/pycodestyle_violations_report.html index 2373ed82..a604e442 100644 --- a/diff_cover/tests/fixtures/pep8_violations_report.html +++ b/diff_cover/tests/fixtures/pycodestyle_violations_report.html @@ -76,7 +76,7 @@

Diff Quality

-

Quality Report: pep8

+

Quality Report: pycodestyle

Diff: origin/master...HEAD, staged and unstaged changes

diff --git a/diff_cover/tests/fixtures/pep8_violations_report.txt b/diff_cover/tests/fixtures/pycodestyle_violations_report.txt similarity index 92% rename from diff_cover/tests/fixtures/pep8_violations_report.txt rename to diff_cover/tests/fixtures/pycodestyle_violations_report.txt index a8953fc2..f56d703a 100644 --- a/diff_cover/tests/fixtures/pep8_violations_report.txt +++ b/diff_cover/tests/fixtures/pycodestyle_violations_report.txt @@ -1,6 +1,6 @@ ------------- Diff Quality -Quality Report: pep8 +Quality Report: pycodestyle Diff: origin/master...HEAD, staged and unstaged changes ------------- violations_test_file.py (66.7%): diff --git a/diff_cover/tests/fixtures/pep8_violations_report_external_css.html b/diff_cover/tests/fixtures/pycodestyle_violations_report_external_css.html similarity index 98% rename from diff_cover/tests/fixtures/pep8_violations_report_external_css.html rename to diff_cover/tests/fixtures/pycodestyle_violations_report_external_css.html index 02c27af3..14739fc4 100644 --- a/diff_cover/tests/fixtures/pep8_violations_report_external_css.html +++ b/diff_cover/tests/fixtures/pycodestyle_violations_report_external_css.html @@ -7,7 +7,7 @@

Diff Quality

-

Quality Report: pep8

+

Quality Report: pycodestyle

Diff: origin/master...HEAD, staged and unstaged changes

diff --git a/diff_cover/tests/helpers.py b/diff_cover/tests/helpers.py index 8e1315d2..90b8c542 100644 --- a/diff_cover/tests/helpers.py +++ b/diff_cover/tests/helpers.py @@ -2,7 +2,6 @@ Test helper functions. """ import random -import sys import io import six @@ -10,11 +9,6 @@ import difflib from nose.tools import ok_ -if sys.version_info[:2] <= (2, 6): - import unittest2 as unittest -else: - import unittest - HUNK_BUFFER = 2 MAX_LINE_LENGTH = 300 LINE_STRINGS = ['test', '+ has a plus sign', '- has a minus sign'] diff --git a/diff_cover/tests/test_args.py b/diff_cover/tests/test_args.py index acf9525b..7c4c7216 100644 --- a/diff_cover/tests/test_args.py +++ b/diff_cover/tests/test_args.py @@ -1,9 +1,9 @@ from __future__ import unicode_literals import contextlib import sys -from mock import Mock, patch +from mock import patch from diff_cover.tool import parse_coverage_args, parse_quality_args, main -from diff_cover.tests.helpers import unittest +import unittest @contextlib.contextmanager @@ -93,12 +93,12 @@ def test_parse_with_exclude(self): class ParseQualityArgsTest(unittest.TestCase): def test_parse_with_html_report(self): - argv = ['--violations', 'pep8', + argv = ['--violations', 'pycodestyle', '--html-report', 'diff_cover.html'] arg_dict = parse_quality_args(argv) - self.assertEqual(arg_dict.get('violations'), 'pep8') + self.assertEqual(arg_dict.get('violations'), 'pycodestyle') self.assertEqual(arg_dict.get('html_report'), 'diff_cover.html') self.assertEqual(arg_dict.get('input_reports'), []) self.assertEqual(arg_dict.get('ignore_unstaged'), False) @@ -131,7 +131,7 @@ def test_parse_with_multiple_input_reports(self): def test_parse_with_options(self): argv = [ - '--violations', 'pep8', + '--violations', 'pycodestyle', '--options="--exclude=\'*/migrations*\'"' ] arg_dict = parse_quality_args(argv) diff --git a/diff_cover/tests/test_diff_reporter.py b/diff_cover/tests/test_diff_reporter.py index d158de8a..44f62637 100644 --- a/diff_cover/tests/test_diff_reporter.py +++ b/diff_cover/tests/test_diff_reporter.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals import mock +import unittest from textwrap import dedent from diff_cover.diff_reporter import GitDiffReporter from diff_cover.git_diff import GitDiffTool, GitDiffError -from diff_cover.tests.helpers import line_numbers, git_diff_output, unittest +from diff_cover.tests.helpers import line_numbers, git_diff_output class GitDiffReporterTest(unittest.TestCase): diff --git a/diff_cover/tests/test_git_diff.py b/diff_cover/tests/test_git_diff.py index 56192a8b..813e8685 100644 --- a/diff_cover/tests/test_git_diff.py +++ b/diff_cover/tests/test_git_diff.py @@ -2,8 +2,8 @@ import mock from diff_cover.command_runner import CommandError -from diff_cover.git_diff import GitDiffTool, GitDiffError -from diff_cover.tests.helpers import unittest +from diff_cover.git_diff import GitDiffTool +import unittest class TestGitDiffTool(unittest.TestCase): diff --git a/diff_cover/tests/test_git_path.py b/diff_cover/tests/test_git_path.py index 1510786b..7389879c 100644 --- a/diff_cover/tests/test_git_path.py +++ b/diff_cover/tests/test_git_path.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import mock from diff_cover.git_path import GitPathTool -from diff_cover.tests.helpers import unittest +import unittest class TestGitPathTool(unittest.TestCase): diff --git a/diff_cover/tests/test_integration.py b/diff_cover/tests/test_integration.py index b9133e27..e5aa5d85 100644 --- a/diff_cover/tests/test_integration.py +++ b/diff_cover/tests/test_integration.py @@ -11,7 +11,7 @@ from collections import defaultdict from io import BytesIO from subprocess import Popen -from diff_cover.tests.helpers import unittest +import unittest import io import six @@ -382,13 +382,13 @@ def test_git_diff_error_diff_quality(self): # Expect an error with self.assertRaises(CommandError): - main(['diff-quality', '--violations', 'pep8']) + main(['diff-quality', '--violations', 'pycodestyle']) - def test_added_file_pep8_html(self): + def test_added_file_pycodestyle_html(self): self._check_html_report( 'git_diff_violations.txt', - 'pep8_violations_report.html', - ['diff-quality', '--violations=pep8'] + 'pycodestyle_violations_report.html', + ['diff-quality', '--violations=pycodestyle'] ) def test_added_file_pyflakes_html(self): @@ -424,24 +424,24 @@ def test_fail_under_pass_html(self): def test_html_with_external_css(self): temp_dir = self._check_html_report( 'git_diff_violations.txt', - 'pep8_violations_report_external_css.html', - ['diff-quality', '--violations=pep8'], + 'pycodestyle_violations_report_external_css.html', + ['diff-quality', '--violations=pycodestyle'], css_file='external_style.css' ) self.assertTrue(os.path.exists(os.path.join(temp_dir, 'external_style.css'))) - def test_added_file_pep8_console(self): + def test_added_file_pycodestyle_console(self): self._check_console_report( 'git_diff_violations.txt', - 'pep8_violations_report.txt', - ['diff-quality', '--violations=pep8'] + 'pycodestyle_violations_report.txt', + ['diff-quality', '--violations=pycodestyle'] ) - def test_added_file_pep8_console_exclude_file(self): + def test_added_file_pycodestyle_console_exclude_file(self): self._check_console_report( 'git_diff_violations.txt', - 'empty_pep8_violations.txt', - ['diff-quality', '--violations=pep8', '--options="--exclude=violations_test_file.py"'] + 'empty_pycodestyle_violations.txt', + ['diff-quality', '--violations=pycodestyle', '--options="--exclude=violations_test_file.py"'] ) def test_fail_under_console(self): @@ -483,14 +483,14 @@ def test_added_file_pylint_console(self): ['diff-quality', '--violations=pylint'], ) - def test_pre_generated_pep8_report(self): + def test_pre_generated_pycodestyle_report(self): - # Pass in a pre-generated pep8 report instead of letting - # the tool call pep8 itself. + # Pass in a pre-generated pycodestyle report instead of letting + # the tool call pycodestyle itself. self._check_console_report( 'git_diff_violations.txt', - 'pep8_violations_report.txt', - ['diff-quality', '--violations=pep8', 'pep8_report.txt'] + 'pycodestyle_violations_report.txt', + ['diff-quality', '--violations=pycodestyle', 'pycodestyle_report.txt'] ) def test_pre_generated_pyflakes_report(self): @@ -564,7 +564,7 @@ def test_do_nothing_reporter(self): # should not do anything # Does demonstrate a reporter can take in any tool # name though which is cool - reporter = DoNothingDriver('pep8', [], []) + reporter = DoNothingDriver('pycodestyle', [], []) self.assertEqual(reporter.parse_reports(''), {}) diff --git a/diff_cover/tests/test_report_generator.py b/diff_cover/tests/test_report_generator.py index 26d3b69b..7f5263a7 100644 --- a/diff_cover/tests/test_report_generator.py +++ b/diff_cover/tests/test_report_generator.py @@ -11,8 +11,9 @@ StringReportGenerator, TemplateReportGenerator ) from diff_cover.tests.helpers import ( - load_fixture, assert_long_str_equal, unittest + load_fixture, assert_long_str_equal ) +import unittest from diff_cover.violationsreporters.violations_reporter import BaseViolationReporter, Violation diff --git a/diff_cover/tests/test_snippets.py b/diff_cover/tests/test_snippets.py index 127e8061..d3b34c54 100644 --- a/diff_cover/tests/test_snippets.py +++ b/diff_cover/tests/test_snippets.py @@ -7,8 +7,9 @@ from pygments.token import Token from diff_cover.snippets import Snippet from diff_cover.tests.helpers import load_fixture,\ - fixture_path, assert_long_str_equal, unittest + fixture_path, assert_long_str_equal import six +import unittest class SnippetTest(unittest.TestCase): diff --git a/diff_cover/tests/test_violations_reporter.py b/diff_cover/tests/test_violations_reporter.py index 6bf16b4b..9ac9b654 100644 --- a/diff_cover/tests/test_violations_reporter.py +++ b/diff_cover/tests/test_violations_reporter.py @@ -12,10 +12,10 @@ from diff_cover.violationsreporters import base from diff_cover.command_runner import CommandError, run_command_for_code -from diff_cover.tests.helpers import unittest +import unittest from diff_cover.violationsreporters.base import QualityReporter from diff_cover.violationsreporters.violations_reporter import ( - XmlCoverageReporter, Violation, pep8_driver, pyflakes_driver, + XmlCoverageReporter, Violation, pycodestyle_driver, pyflakes_driver, flake8_driver, PylintDriver, jshint_driver, eslint_driver, pydocstyle_driver) from mock import Mock, patch, MagicMock @@ -320,7 +320,7 @@ def _coverage_xml( return root -class Pep8QualityReporterTest(unittest.TestCase): +class pycodestyleQualityReporterTest(unittest.TestCase): def setUp(self): _patch_so_all_files_exist() @@ -333,7 +333,7 @@ def tearDown(self): def test_quality(self): - # Patch the output of `pep8` + # Patch the output of `pycodestyle` _mock_communicate = patch.object(Popen, 'communicate').start() return_string = '\n' + dedent(""" ../new_file.py:1:17: E231 whitespace @@ -343,10 +343,10 @@ def test_quality(self): _setup_patch((return_string.encode('utf-8'), b'')) # Parse the report - quality = QualityReporter(pep8_driver) + quality = QualityReporter(pycodestyle_driver) # Expect that the name is set - self.assertEqual(quality.name(), 'pep8') + self.assertEqual(quality.name(), 'pycodestyle') # Measured_lines is undefined for # a quality reporter since all lines are measured @@ -363,46 +363,46 @@ def test_quality(self): def test_no_quality_issues_newline(self): - # Patch the output of `pep8` + # Patch the output of `pycodestyle` _setup_patch((b'\n', b'')) # Parse the report - quality = QualityReporter(pep8_driver) + quality = QualityReporter(pycodestyle_driver) self.assertEqual([], quality.violations('file1.py')) def test_no_quality_issues_emptystring(self): - # Patch the output of `pep8` + # Patch the output of `pycodestyle` _setup_patch((b'', b'')) # Parse the report - quality = QualityReporter(pep8_driver) + quality = QualityReporter(pycodestyle_driver) self.assertEqual([], quality.violations('file1.py')) def test_quality_error(self): - # Patch the output of `pep8` + # Patch the output of `pycodestyle` _setup_patch((b"", 'whoops Ƕئ'.encode('utf-8')), status_code=1) with patch('diff_cover.violationsreporters.base.run_command_for_code') as code: code.return_value = 0 # Parse the report - quality = QualityReporter(pep8_driver) + quality = QualityReporter(pycodestyle_driver) # Expect that the name is set - self.assertEqual(quality.name(), 'pep8') + self.assertEqual(quality.name(), 'pycodestyle') with self.assertRaises(CommandError) as ex: quality.violations('file1.py') self.assertEqual(six.text_type(ex.exception), 'whoops Ƕئ') def test_no_such_file(self): - quality = QualityReporter(pep8_driver) + quality = QualityReporter(pycodestyle_driver) # Expect that we get no results result = quality.violations('') self.assertEqual(result, []) def test_no_python_file(self): - quality = QualityReporter(pep8_driver) + quality = QualityReporter(pycodestyle_driver) file_paths = ['file1.coffee', 'subdir/file2.js'] # Expect that we get no results because no Python files for path in file_paths: @@ -411,9 +411,9 @@ def test_no_python_file(self): def test_quality_pregenerated_report(self): - # When the user provides us with a pre-generated pep8 report - # then use that instead of calling pep8 directly. - pep8_reports = [ + # When the user provides us with a pre-generated pycodestyle report + # then use that instead of calling pycodestyle directly. + pycodestyle_reports = [ BytesIO(('\n' + dedent(""" path/to/file.py:1:17: E231 whitespace path/to/file.py:3:13: E225 whitespace @@ -427,7 +427,7 @@ def test_quality_pregenerated_report(self): ] # Parse the report - quality = QualityReporter(pep8_driver, reports=pep8_reports) + quality = QualityReporter(pycodestyle_driver, reports=pycodestyle_reports) # Measured_lines is undefined for # a quality reporter since all lines are measured @@ -1231,8 +1231,8 @@ def test_quality_reporter(self, mock_stderr): _patch_so_all_files_exist() with patch('diff_cover.violationsreporters.base.run_command_for_code') as code: code.return_value = 0 - reporter = QualityReporter(pep8_driver) + reporter = QualityReporter(pycodestyle_driver) with self.assertRaises(OSError): reporter.violations("path/to/file.py") - self.assertEqual(mock_stderr.getvalue(), "pep8 path/to/file.py") + self.assertEqual(mock_stderr.getvalue(), "pycodestyle path/to/file.py") diff --git a/diff_cover/tool.py b/diff_cover/tool.py index cea52fd0..5828706b 100644 --- a/diff_cover/tool.py +++ b/diff_cover/tool.py @@ -22,12 +22,12 @@ from diff_cover.violationsreporters.base import QualityReporter from diff_cover.violationsreporters.violations_reporter import ( XmlCoverageReporter, - flake8_driver, pyflakes_driver, pep8_driver, PylintDriver, - jshint_driver, eslint_driver, pydocstyle_driver -) + flake8_driver, pyflakes_driver, PylintDriver, + jshint_driver, eslint_driver, pydocstyle_driver, + pycodestyle_driver) QUALITY_DRIVERS = { - 'pep8': pep8_driver, + 'pycodestyle': pycodestyle_driver, 'pyflakes': pyflakes_driver, 'pylint': PylintDriver(), 'flake8': flake8_driver, @@ -139,7 +139,7 @@ def parse_quality_args(argv): valid options: { - 'violations': pep8 | pyflakes | flake8 | pylint | ..., + 'violations': pycodestyle| pyflakes | flake8 | pylint | ..., 'html_report': None | HTML_REPORT, 'external_css_file': None | CSS_FILE, } diff --git a/diff_cover/violationsreporters/violations_reporter.py b/diff_cover/violationsreporters/violations_reporter.py index 0a659309..39b341dd 100644 --- a/diff_cover/violationsreporters/violations_reporter.py +++ b/diff_cover/violationsreporters/violations_reporter.py @@ -184,12 +184,12 @@ def measured_lines(self, src_path): self._cache_file(src_path) return self._info_cache[src_path][1] -pep8_driver = RegexBasedDriver( - name='pep8', +pycodestyle_driver = RegexBasedDriver( + name='pycodestyle', supported_extensions=['py'], - command=['pep8'], + command=['pycodestyle'], expression=r'^([^:]+):(\d+).*([EW]\d{3}.*)$', - command_to_check_install=['pep8', '--version'] + command_to_check_install=['pycodestyle', '--version'] ) pyflakes_driver = RegexBasedDriver( @@ -207,10 +207,10 @@ def measured_lines(self, src_path): Report Flake8 violations. Flake8 warning/error codes: - E***/W***: pep8 errors and warnings + E***/W***: pycodestyle errors and warnings F***: pyflakes codes C9**: mccabe complexity plugin - N8**: pep8-naming plugin + N8**: pycodestyle-naming plugin T000: flake8-todo plugin http://flake8.readthedocs.org/en/latest/warnings.html diff --git a/requirements/test-requirements-py26.txt b/requirements/test-requirements-py26.txt deleted file mode 100644 index 5fb707bd..00000000 --- a/requirements/test-requirements-py26.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Requirements for developing on python 2.6 -astroid<=1.3.2 # rq.filter: >=1.3.2,<1.3.3 -pylint<=1.3.0 # rq.filter: >=1.3,<1.3.1 --r test-requirements.txt diff --git a/requirements/test-requirements-py27-py3.txt b/requirements/test-requirements-py27-py3.txt deleted file mode 100644 index 41741769..00000000 --- a/requirements/test-requirements-py27-py3.txt +++ /dev/null @@ -1,4 +0,0 @@ -# Requirements for developing on python 2.7 though python 3.4 -pylint -pydocstyle --r test-requirements.txt diff --git a/requirements/test-requirements.txt b/requirements/test-requirements.txt index 1b5252b7..30316acf 100644 --- a/requirements/test-requirements.txt +++ b/requirements/test-requirements.txt @@ -2,7 +2,9 @@ mock nose coverage -pep8 +pycodestyle flake8 pyflakes +pylint +pydocstyle -r requirements.txt diff --git a/tox.ini b/tox.ini index c390ecb7..c8912f0d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,26 +1,20 @@ [tox] -envlist = py26, py27, py33, py34, py35, pypy +envlist = py27, py34, py35, py36, pypy, pypy3 [testenv] whitelist_externals = /usr/bin/git deps = - -r{toxinidir}/requirements/test-requirements-py27-py3.txt + -r{toxinidir}/requirements/test-requirements.txt commands = coverage run -m nose {posargs} coverage xml git fetch origin master:refs/remotes/origin/master diff-cover coverage.xml - diff-quality --violation=pep8 + diff-quality --violation=pycodestyle diff-quality --violation=pyflakes diff-quality --violation=pylint -[testenv:py26] -deps = - -r{toxinidir}/requirements/test-requirements-py26.txt - argparse - unittest2 - [testenv:py27] commands = {[testenv]commands}