Commit c78ccf8f by John Eskew

Fix the systems parsing code for pylint.

Add lower pylint error threshold to guard against unsuccessful runs.
Increase upper pylint threshold. Add tests for pylint option parsing.
parent a4474385
...@@ -8,7 +8,7 @@ import unittest ...@@ -8,7 +8,7 @@ import unittest
import paver.easy import paver.easy
import paver.tasks import paver.tasks
from ddt import ddt, file_data from ddt import ddt, file_data, data, unpack
from mock import MagicMock, mock_open, patch from mock import MagicMock, mock_open, patch
from path import Path as path from path import Path as path
from paver.easy import BuildFailure from paver.easy import BuildFailure
...@@ -60,6 +60,34 @@ class TestPaverQualityViolations(unittest.TestCase): ...@@ -60,6 +60,34 @@ class TestPaverQualityViolations(unittest.TestCase):
self.assertEqual(num, 2) self.assertEqual(num, 2)
@ddt
class TestPaverQualityOptions(unittest.TestCase):
"""
Tests the paver pylint command-line options parsing.
"""
@data(
({'limit': '5500'}, (-1, 5500, False, pavelib.quality.ALL_SYSTEMS.split(','))),
({'limit': '1000:5500'}, (1000, 5500, False, pavelib.quality.ALL_SYSTEMS.split(','))),
({'limit': '1:2:3:4:5'}, (1, 2, False, pavelib.quality.ALL_SYSTEMS.split(','))),
({'system': 'lms,cms'}, (-1, -1, False, ['lms', 'cms'])),
(
{'limit': '2000:5000', 'errors': True, 'system': 'lms,cms,openedx'},
(2000, 5000, True, ['lms', 'cms', 'openedx'])
),
)
@unpack
def test_pylint_parser_other_string(self, options, expected_values):
class PaverOptions(object):
"""
Simple options class to mimick paver's Namespace object.
"""
def __init__(self, d):
self.__dict__ = d
paver_options = PaverOptions(options)
returned_values = pavelib.quality._parse_pylint_options(paver_options) # pylint: disable=protected-access
self.assertEqual(returned_values, expected_values)
class TestPaverReportViolationsCounts(unittest.TestCase): class TestPaverReportViolationsCounts(unittest.TestCase):
""" """
For testing utility functions for getting counts from reports for For testing utility functions for getting counts from reports for
......
...@@ -6,7 +6,6 @@ Check code quality using pep8, pylint, and diff_quality. ...@@ -6,7 +6,6 @@ Check code quality using pep8, pylint, and diff_quality.
import json import json
import os import os
import re import re
from string import join
from paver.easy import BuildFailure, call_task, cmdopts, needs, sh, task from paver.easy import BuildFailure, call_task, cmdopts, needs, sh, task
...@@ -15,13 +14,7 @@ from openedx.core.djangolib.markup import HTML ...@@ -15,13 +14,7 @@ from openedx.core.djangolib.markup import HTML
from .utils.envs import Env from .utils.envs import Env
from .utils.timer import timed from .utils.timer import timed
ALL_SYSTEMS = [ ALL_SYSTEMS = 'lms,cms,common,openedx,pavelib'
'cms',
'common',
'lms',
'openedx',
'pavelib',
]
def top_python_dirs(dirname): def top_python_dirs(dirname):
...@@ -55,7 +48,7 @@ def find_fixme(options): ...@@ -55,7 +48,7 @@ def find_fixme(options):
Run pylint on system code, only looking for fixme items. Run pylint on system code, only looking for fixme items.
""" """
num_fixme = 0 num_fixme = 0
systems = getattr(options, 'system', '').split(',') or ALL_SYSTEMS systems = getattr(options, 'system', ALL_SYSTEMS).split(',')
for system in systems: for system in systems:
# Directory to put the pylint report in. # Directory to put the pylint report in.
...@@ -92,7 +85,7 @@ def find_fixme(options): ...@@ -92,7 +85,7 @@ def find_fixme(options):
@cmdopts([ @cmdopts([
("system=", "s", "System to act on"), ("system=", "s", "System to act on"),
("errors", "e", "Check for errors only"), ("errors", "e", "Check for errors only"),
("limit=", "l", "limit for number of acceptable violations"), ("limit=", "l", "Limits for number of acceptable violations - either <upper> or <lower>:<upper>"),
]) ])
@timed @timed
def run_pylint(options): def run_pylint(options):
...@@ -100,14 +93,12 @@ def run_pylint(options): ...@@ -100,14 +93,12 @@ def run_pylint(options):
Run pylint on system code. When violations limit is passed in, Run pylint on system code. When violations limit is passed in,
fail the task if too many violations are found. fail the task if too many violations are found.
""" """
num_violations = 0 lower_violations_limit, upper_violations_limit, errors, systems = _parse_pylint_options(options)
violations_limit = int(getattr(options, 'limit', -1))
errors = getattr(options, 'errors', False)
systems = getattr(options, 'system', '').split(',') or ALL_SYSTEMS
# Make sure the metrics subdirectory exists # Make sure the metrics subdirectory exists
Env.METRICS_DIR.makedirs_p() Env.METRICS_DIR.makedirs_p()
num_violations = 0
for system in systems: for system in systems:
# Directory to put the pylint report in. # Directory to put the pylint report in.
# This makes the folder if it doesn't already exist. # This makes the folder if it doesn't already exist.
...@@ -147,10 +138,45 @@ def run_pylint(options): ...@@ -147,10 +138,45 @@ def run_pylint(options):
with open(Env.METRICS_DIR / "pylint", "w") as f: with open(Env.METRICS_DIR / "pylint", "w") as f:
f.write(violations_count_str) f.write(violations_count_str)
# Fail number of violations is greater than the limit # Fail when number of violations is less than the lower limit,
if num_violations > violations_limit > -1: # which likely means that pylint did not run successfully.
raise BuildFailure("Failed. Too many pylint violations. " # If pylint *did* run successfully, then great! Modify the lower limit.
"The limit is {violations_limit}.".format(violations_limit=violations_limit)) if num_violations < lower_violations_limit > -1:
raise BuildFailure(
"Failed. Too few pylint violations. "
"Expected to see at least {lower_limit} pylint violations. "
"Either pylint is not running correctly -or- "
"the limits should be lowered and/or the lower limit should be removed.".format(
lower_limit=lower_violations_limit
)
)
# Fail when number of violations is greater than the upper limit.
if num_violations > upper_violations_limit > -1:
raise BuildFailure(
"Failed. Too many pylint violations. "
"The limit is {upper_limit}.".format(upper_limit=upper_violations_limit)
)
def _parse_pylint_options(options):
"""
Parse the options passed to run_pylint.
"""
lower_violations_limit = upper_violations_limit = -1
violations_limit = getattr(options, 'limit', '').split(':')
if violations_limit[0]:
# Limit was specified.
if len(violations_limit) == 1:
# Only upper limit was specified.
upper_violations_limit = int(violations_limit[0])
else:
# Upper and lower limits were both specified.
lower_violations_limit = int(violations_limit[0])
upper_violations_limit = int(violations_limit[1])
errors = getattr(options, 'errors', False)
systems = getattr(options, 'system', ALL_SYSTEMS).split(',')
return lower_violations_limit, upper_violations_limit, errors, systems
def _count_pylint_violations(report_file): def _count_pylint_violations(report_file):
...@@ -244,7 +270,7 @@ def run_complexity(): ...@@ -244,7 +270,7 @@ def run_complexity():
Uses radon to examine cyclomatic complexity. Uses radon to examine cyclomatic complexity.
For additional details on radon, see http://radon.readthedocs.org/ For additional details on radon, see http://radon.readthedocs.org/
""" """
system_string = join(ALL_SYSTEMS, '/ ') + '/' system_string = '/ '.join(ALL_SYSTEMS.split(',')) + '/'
complexity_report_dir = (Env.REPORT_DIR / "complexity") complexity_report_dir = (Env.REPORT_DIR / "complexity")
complexity_report = complexity_report_dir / "python_complexity.log" complexity_report = complexity_report_dir / "python_complexity.log"
......
...@@ -11,7 +11,8 @@ set -e ...@@ -11,7 +11,8 @@ set -e
############################################################################### ###############################################################################
# Violations thresholds for failing the build # Violations thresholds for failing the build
export PYLINT_THRESHOLD=3600 export LOWER_PYLINT_THRESHOLD=1000
export UPPER_PYLINT_THRESHOLD=5335
export ESLINT_THRESHOLD=9134 export ESLINT_THRESHOLD=9134
export STYLELINT_THRESHOLD=973 export STYLELINT_THRESHOLD=973
......
...@@ -57,7 +57,7 @@ else ...@@ -57,7 +57,7 @@ else
echo "Finding pylint violations and storing in report..." echo "Finding pylint violations and storing in report..."
# HACK: we need to print something to the console, otherwise circleci # HACK: we need to print something to the console, otherwise circleci
# fails and aborts the job because nothing is displayed for > 10 minutes. # fails and aborts the job because nothing is displayed for > 10 minutes.
paver run_pylint -l $PYLINT_THRESHOLD | tee pylint.log || EXIT=1 paver run_pylint -l $LOWER_PYLINT_THRESHOLD:$UPPER_PYLINT_THRESHOLD | tee pylint.log || EXIT=1
mkdir -p reports mkdir -p reports
PATH=$PATH:node_modules/.bin PATH=$PATH:node_modules/.bin
......
...@@ -84,7 +84,7 @@ case "$TEST_SUITE" in ...@@ -84,7 +84,7 @@ case "$TEST_SUITE" in
echo "Finding pep8 violations and storing report..." echo "Finding pep8 violations and storing report..."
paver run_pep8 > pep8.log || { cat pep8.log; EXIT=1; } paver run_pep8 > pep8.log || { cat pep8.log; EXIT=1; }
echo "Finding pylint violations and storing in report..." echo "Finding pylint violations and storing in report..."
paver run_pylint -l $PYLINT_THRESHOLD > pylint.log || { echo 'Too many pylint violations. You can view them in pylint.log'; EXIT=1; } paver run_pylint -l $LOWER_PYLINT_THRESHOLD:$UPPER_PYLINT_THRESHOLD > pylint.log || { echo 'Too many pylint violations. You can view them in pylint.log'; EXIT=1; }
mkdir -p reports mkdir -p reports
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment