Merge branch 'christianmlong/issue/8'

This commit is contained in:
Ian Cordasco 2015-03-07 20:22:11 -06:00
commit a66815ffa6
6 changed files with 242 additions and 7 deletions

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import re
import platform
import warnings
import pep8
@ -8,7 +9,7 @@ from flake8 import __version__
from flake8 import callbacks
from flake8.reporter import (multiprocessing, BaseQReport, FileQReport,
QueueReport)
from flake8.util import OrderedSet, is_windows, is_using_stdin
from flake8 import util
_flake8_noqa = re.compile(r'\s*# flake8[:=]\s*noqa', re.I).search
@ -17,7 +18,7 @@ EXTRA_EXCLUDE = ['.tox', '.eggs', '*.egg']
def _register_extensions():
"""Register all the extensions."""
extensions = OrderedSet()
extensions = util.OrderedSet()
extensions.add(('pep8', pep8.__version__))
parser_hooks = []
options_hooks = []
@ -123,10 +124,25 @@ def get_style_guide(**kwargs):
for options_hook in options_hooks:
options_hook(options)
if util.warn_when_using_jobs(options):
if not multiprocessing:
warnings.warn("The multiprocessing module is not available. "
"Ignoring --jobs arguments.")
if util.is_windows():
warnings.warn("The --jobs option is not available on Windows. "
"Ignoring --jobs arguments.")
if util.is_using_stdin(styleguide.paths):
warnings.warn("The --jobs option is not compatible with supplying "
"input using - . Ignoring --jobs arguments.")
if options.diff:
warnings.warn("The --diff option was specified with --jobs but "
"they are not compatible. Ignoring --jobs arguments."
)
if options.diff:
options.jobs = None
force_disable_jobs = is_windows() or is_using_stdin(styleguide.paths)
force_disable_jobs = util.force_disable_jobs(styleguide)
if multiprocessing and options.jobs and not force_disable_jobs:
if options.jobs.isdigit():

View file

@ -39,6 +39,7 @@ class TestEngine(unittest.TestCase):
with mock.patch('flake8.engine.get_parser') as get_parser:
m.ignored_extensions = []
StyleGuide.return_value.options.jobs = '42'
StyleGuide.return_value.options.diff = False
get_parser.return_value = (m, [])
engine.get_style_guide(foo='bar')
get_parser.assert_called_once_with()
@ -85,13 +86,13 @@ class TestEngine(unittest.TestCase):
# ourselves) what system we may be testing on.
def test_windows_disables_jobs(self):
with mock.patch('flake8.engine.is_windows') as is_windows:
with mock.patch('flake8.util.is_windows') as is_windows:
is_windows.return_value = True
guide = engine.get_style_guide()
assert isinstance(guide, reporter.BaseQReport) is False
def test_stdin_disables_jobs(self):
with mock.patch('flake8.engine.is_using_stdin') as is_using_stdin:
with mock.patch('flake8.util.is_using_stdin') as is_using_stdin:
is_using_stdin.return_value = True
guide = engine.get_style_guide()
assert isinstance(guide, reporter.BaseQReport) is False

View file

@ -8,6 +8,7 @@ except ImportError:
import mock # < PY33
from flake8 import engine
from flake8.util import is_windows
class IntegrationTestCase(unittest.TestCase):
@ -41,8 +42,15 @@ class IntegrationTestCase(unittest.TestCase):
# mock stdout.flush so we can count the number of jobs created
with mock.patch('sys.stdout.flush') as mocked:
guide, report = self.check_files(arglist=['--jobs=%s' % jobs])
self.assertEqual(guide.options.jobs, jobs)
self.assertEqual(mocked.call_count, jobs)
if is_windows():
# The code path where guide.options.jobs gets converted to an
# int is not run on windows. So, do the int conversion here.
self.assertEqual(int(guide.options.jobs), jobs)
# On windows, call count is always zero.
self.assertEqual(mocked.call_count, 0)
else:
self.assertEqual(guide.options.jobs, jobs)
self.assertEqual(mocked.call_count, jobs)
def test_jobs(self):
self._job_tester(2)

View file

@ -0,0 +1,200 @@
"""
_test_warnings.py
Tests for the warnings that are emitted by flake8.
This module is named _test_warnings instead of test_warnings so that a
normal nosetests run does not collect it. The tests in this module pass
when they are run alone, but they fail when they are run along with other
tests (nosetests --with-isolation doesn't help).
In tox.ini, these tests are run separately.
"""
from __future__ import with_statement
import os
import warnings
import unittest
try:
from unittest import mock
except ImportError:
import mock # < PY33
from flake8 import engine
from flake8.util import is_windows
class IntegrationTestCaseWarnings(unittest.TestCase):
"""Integration style tests to check that warnings are issued properly for
different command line options."""
windows_warning_text = ("The --jobs option is not available on Windows."
" Ignoring --jobs arguments.")
stdin_warning_text = ("The --jobs option is not compatible with"
" supplying input using - . Ignoring --jobs"
" arguments.")
def this_file(self):
"""Return the real path of this file."""
this_file = os.path.realpath(__file__)
if this_file.endswith("pyc"):
this_file = this_file[:-1]
return this_file
@staticmethod
def get_style_guide_with_warnings(engine, *args, **kwargs):
"""
Return a style guide object (obtained by calling
engine.get_style_guide) and a list of the warnings that were raised in
the process.
Note: not threadsafe
"""
# Note
# https://docs.python.org/2/library/warnings.html
#
# The catch_warnings manager works by replacing and then later
# restoring the module's showwarning() function and internal list of
# filter specifications. This means the context manager is modifying
# global state and therefore is not thread-safe
with warnings.catch_warnings(record=True) as collected_warnings:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
# Get the style guide
style_guide = engine.get_style_guide(*args, **kwargs)
# Now that the warnings have been collected, return the style guide and
# the
# warnings.
return (style_guide, collected_warnings)
def verify_warnings(self, collected_warnings, expected_warnings):
"""
Verifies that collected_warnings is a sequence that contains user
warnings that match the sequence of string values passed in as
expected_warnings.
"""
if expected_warnings is None:
expected_warnings = []
collected_user_warnings = [w for w in collected_warnings
if issubclass(w.category, UserWarning)]
self.assertEqual(len(collected_user_warnings),
len(expected_warnings))
collected_warnings_set = set(str(warning.message)
for warning
in collected_user_warnings)
expected_warnings_set = set(expected_warnings)
self.assertEqual(collected_warnings_set, expected_warnings_set)
def check_files_collect_warnings(self,
arglist=[],
explicit_stdin=False,
count=0,
verbose=False):
"""Call check_files and collect any warnings that are issued."""
if verbose:
arglist.append('--verbose')
if explicit_stdin:
target_file = "-"
else:
target_file = self.this_file()
argv = ['flake8'] + arglist + [target_file]
with mock.patch("sys.argv", argv):
(style_guide,
collected_warnings,
) = self.get_style_guide_with_warnings(engine,
parse_argv=True)
report = style_guide.check_files()
self.assertEqual(report.total_errors, count)
return style_guide, report, collected_warnings
def check_files_no_warnings_allowed(self,
arglist=[],
explicit_stdin=False,
count=0,
verbose=False):
"""Call check_files, and assert that there were no warnings issued."""
(style_guide,
report,
collected_warnings,
) = self.check_files_collect_warnings(arglist=arglist,
explicit_stdin=explicit_stdin,
count=count,
verbose=verbose)
self.verify_warnings(collected_warnings, expected_warnings=None)
return style_guide, report
def _job_tester(self, jobs, verbose=False):
# mock stdout.flush so we can count the number of jobs created
with mock.patch('sys.stdout.flush') as mocked:
(guide,
report,
collected_warnings,
) = self.check_files_collect_warnings(
arglist=['--jobs=%s' % jobs],
verbose=verbose)
if is_windows():
# The code path where guide.options.jobs gets converted to an
# int is not run on windows. So, do the int conversion here.
self.assertEqual(int(guide.options.jobs), jobs)
# On windows, call count is always zero.
self.assertEqual(mocked.call_count, 0)
else:
self.assertEqual(guide.options.jobs, jobs)
self.assertEqual(mocked.call_count, jobs)
expected_warings = []
if verbose and is_windows():
expected_warings.append(self.windows_warning_text)
self.verify_warnings(collected_warnings, expected_warings)
def test_jobs(self, verbose=False):
self._job_tester(2, verbose=verbose)
self._job_tester(10, verbose=verbose)
def test_no_args_no_warnings(self, verbose=False):
self.check_files_no_warnings_allowed(verbose=verbose)
def test_stdin_jobs_warning(self, verbose=False):
self.count = 0
def fake_stdin():
self.count += 1
with open(self.this_file(), "r") as f:
return f.read()
with mock.patch("pep8.stdin_get_value", fake_stdin):
(style_guide,
report,
collected_warnings,
) = self.check_files_collect_warnings(arglist=['--jobs=4'],
explicit_stdin=True,
verbose=verbose)
expected_warings = []
if verbose:
expected_warings.append(self.stdin_warning_text)
if is_windows():
expected_warings.append(self.windows_warning_text)
self.verify_warnings(collected_warnings, expected_warings)
self.assertEqual(self.count, 1)
def test_jobs_verbose(self):
self.test_jobs(verbose=True)
def test_no_args_no_warnings_verbose(self):
self.test_no_args_no_warnings(verbose=True)
def test_stdin_jobs_warning_verbose(self):
self.test_stdin_jobs_warning(verbose=True)
if __name__ == '__main__':
unittest.main()

View file

@ -54,6 +54,15 @@ def is_using_stdin(paths):
return '-' in paths
def warn_when_using_jobs(options):
return (options.verbose and options.jobs and options.jobs.isdigit() and
int(options.jobs) > 1)
def force_disable_jobs(styleguide):
return is_windows() or is_using_stdin(styleguide.paths)
def flag_on(val):
"""Return true if flag is on"""
return str(val).upper() in ('1', 'T', 'TRUE', 'ON')

View file

@ -6,6 +6,7 @@ envlist =
[testenv]
usedevelop = True
deps =
mock
commands =
python setup.py test -q
python setup.py flake8