Skip to content

Commit

Permalink
Merge pull request #64 from mr-c/pydocstyle
Browse files Browse the repository at this point in the history
add pydocstyle support
  • Loading branch information
Bachmann1234 committed Jul 21, 2017
2 parents b154751 + fee69a9 commit 68376ac
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 12 deletions.
67 changes: 65 additions & 2 deletions diff_cover/tests/test_violations_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
from diff_cover.command_runner import CommandError, run_command_for_code
from diff_cover.tests.helpers import unittest
from diff_cover.violationsreporters.base import QualityReporter
from diff_cover.violationsreporters.violations_reporter import XmlCoverageReporter, Violation, pep8_driver, \
pyflakes_driver, flake8_driver, PylintDriver, jshint_driver, eslint_driver
from diff_cover.violationsreporters.violations_reporter import (
XmlCoverageReporter, Violation, pep8_driver, pyflakes_driver,
flake8_driver, PylintDriver, jshint_driver, eslint_driver,
pydocstyle_driver)
from mock import Mock, patch, MagicMock
from six import BytesIO, StringIO

Expand Down Expand Up @@ -727,6 +729,67 @@ def test_quality_pregenerated_report(self):
self.assertIn(expected, actual_violations)


class PydocstlyeQualityReporterTest(unittest.TestCase):
"""Tests for pydocstyle quality violations."""

def setUp(self):
"""Set up required files."""
_patch_so_all_files_exist()

def tearDown(self):
"""Undo all patches."""
patch.stopall()

def test_no_such_file(self):
"""Expect that we get no results."""
quality = QualityReporter(pydocstyle_driver)

result = quality.violations('')
self.assertEqual(result, [])

def test_no_python_file(self):
"""Expect that we get no results because no Python files."""
quality = QualityReporter(pydocstyle_driver)
file_paths = ['file1.coffee', 'subdir/file2.js']
for path in file_paths:
result = quality.violations(path)
self.assertEqual(result, [])

def test_quality(self):
"""Integration test."""
# Patch the output of `pydocstye`
_setup_patch((
dedent("""
../new_file.py:1 at module level:
D100: Missing docstring in public module
../new_file.py:13 in public function `gather`:
D103: Missing docstring in public function
""").strip().encode('ascii'), ''
))

expected_violations = [
Violation(1, 'D100: Missing docstring in public module'),
Violation(13, "D103: Missing docstring in public function"),
]

# Parse the report
quality = QualityReporter(pydocstyle_driver)

# Expect that the name is set
self.assertEqual(quality.name(), 'pydocstyle')

# Measured_lines is undefined for a
# quality reporter since all lines are measured
self.assertEqual(quality.measured_lines('../new_file.py'), None)

# Expect that we get violations for file1.py only
# We're not guaranteed that the violations are returned
# in any particular order.
actual_violations = quality.violations('../new_file.py')
self.assertEqual(len(actual_violations), len(expected_violations))
for expected in expected_violations:
self.assertIn(expected, actual_violations)

class PylintQualityReporterTest(unittest.TestCase):

def setUp(self):
Expand Down
5 changes: 3 additions & 2 deletions diff_cover/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from diff_cover.violationsreporters.violations_reporter import (
XmlCoverageReporter,
flake8_driver, pyflakes_driver, pep8_driver, PylintDriver,
jshint_driver, eslint_driver
jshint_driver, eslint_driver, pydocstyle_driver
)

QUALITY_DRIVERS = {
Expand All @@ -32,7 +32,8 @@
'pylint': PylintDriver(),
'flake8': flake8_driver,
'jshint': jshint_driver,
'eslint': eslint_driver
'eslint': eslint_driver,
'pydocstyle': pydocstyle_driver
}

COVERAGE_XML_HELP = "XML coverage report"
Expand Down
20 changes: 13 additions & 7 deletions diff_cover/violationsreporters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,20 @@ def __init__(
supported_extensions,
command,
expression,
command_to_check_install
command_to_check_install,
flags=0
):
"""
args:
expression: regex used to parse report
expression: regex used to parse report, will be fed lines singly
unless flags contain re.MULTILINE
flags: such as re.MULTILINE
See super for other args
command_to_check_install: (list[str]) command to run
to see if the tool is installed
"""
super(RegexBasedDriver, self).__init__(name, supported_extensions, command)
self.expression = re.compile(expression)
self.expression = re.compile(expression, flags)
self.command_to_check_install = command_to_check_install
self.is_installed = None

Expand All @@ -207,16 +210,19 @@ def parse_reports(self, reports):
"""
violations_dict = defaultdict(list)
for report in reports:
for line in report.split('\n'):
match = self.expression.match(line)
# Ignore any line that isn't a violation
if self.expression.flags & re.MULTILINE:
matches = (match for match in
re.finditer(self.expression, report))
else:
matches = (self.expression.match(line) for line in
report.split('\n'))
for match in matches:
if match is not None:
src, line_number, message = match.groups()
# Transform src to a relative path, if it isn't already
src = os.path.relpath(src)
violation = Violation(int(line_number), message)
violations_dict[src].append(violation)

return violations_dict

def installed(self):
Expand Down
19 changes: 19 additions & 0 deletions diff_cover/violationsreporters/violations_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,25 @@ def measured_lines(self, src_path):
command_to_check_install=['eslint', '-v'],
)

"""
Report pydocstyle violations.
Warning/error codes:
D1**: Missing Docstrings
D2**: Whitespace Issues
D3**: Quotes Issues
D4**: Docstring Content Issues
http://www.pydocstyle.org/en/latest/error_codes.html
"""
pydocstyle_driver = RegexBasedDriver(
name='pydocstyle',
supported_extensions=['py'],
command=['pydocstyle'],
expression=r'^(.+?):(\d+).*?$.+?^ (.*?)$',
command_to_check_install=['pydocstyle', '--version'],
flags=re.MULTILINE | re.DOTALL
)

class PylintDriver(QualityDriver):
def __init__(self):
Expand Down
3 changes: 2 additions & 1 deletion requirements/test-requirements-py27-py3.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Requirements for developing on python 2.7 though python 3.4
pylint
-r test-requirements.txt
pydocstyle
-r test-requirements.txt
6 changes: 6 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ deps =
-r{toxinidir}/requirements/test-requirements-py26.txt
argparse
unittest2

[testenv:py27]
commands =
{[testenv]commands}
diff-quality --violation=pydocstyle

0 comments on commit 68376ac

Please sign in to comment.