From 9d734158ca72c264810c6a4c2d22c3c3160864b1 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Sat, 22 Aug 2015 19:44:44 -0500 Subject: [PATCH] Add tests around the OSError retry logic This updates our retry logic to be more specific in catching OSErrors and it adds specific tests to show that it works and properly re-initializes the StyleGuide with the pep8.StandardReport class so we can fall back on serial behaviour gracefully. Closes #74 --- flake8/engine.py | 9 ++-- flake8/tests/test_engine.py | 84 +++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/flake8/engine.py b/flake8/engine.py index 9cbdf6d..0f440c1 100644 --- a/flake8/engine.py +++ b/flake8/engine.py @@ -120,7 +120,8 @@ class StyleGuide(object): ]) def __init__(self, **kwargs): - self._styleguide = NoQAStyleGuide(**kwargs) + # This allows us to inject a mocked StyleGuide in the tests. + self._styleguide = kwargs.pop('styleguide', NoQAStyleGuide(**kwargs)) @property def options(self): @@ -139,8 +140,8 @@ class StyleGuide(object): """ try: return func(*args, **kwargs) - except IOError as ioerr: - if ioerr.errno in self.serial_retry_errors: + except OSError as oserr: + if oserr.errno in self.serial_retry_errors: self.init_report(pep8.StandardReport) else: raise @@ -161,7 +162,7 @@ class StyleGuide(object): filename=filename, lines=lines, expected=expected, - line_offset=0, + line_offset=line_offset, ) diff --git a/flake8/tests/test_engine.py b/flake8/tests/test_engine.py index 60b388c..a6faab5 100644 --- a/flake8/tests/test_engine.py +++ b/flake8/tests/test_engine.py @@ -1,5 +1,6 @@ from __future__ import with_statement +import errno import unittest try: from unittest import mock @@ -7,6 +8,7 @@ except ImportError: import mock # < PY33 from flake8 import engine, util, __version__, reporter +import pep8 class TestEngine(unittest.TestCase): @@ -112,5 +114,87 @@ class TestEngine(unittest.TestCase): assert 'X' not in sg.options.ignore +def oserror_generator(error_number, message='Ominous OSError message'): + def oserror_side_effect(*args, **kwargs): + if hasattr(oserror_side_effect, 'used'): + return + + oserror_side_effect.used = True + raise OSError(error_number, message) + + return oserror_side_effect + + +class TestStyleGuide(unittest.TestCase): + def setUp(self): + mocked_styleguide = mock.Mock(spec=engine.NoQAStyleGuide) + self.styleguide = engine.StyleGuide(styleguide=mocked_styleguide) + self.mocked_sg = mocked_styleguide + + def test_proxies_excluded(self): + self.styleguide.excluded('file.py', parent='.') + + self.mocked_sg.excluded.assert_called_once_with('file.py', parent='.') + + def test_proxies_init_report(self): + reporter = object() + self.styleguide.init_report(reporter) + + self.mocked_sg.init_report.assert_called_once_with(reporter) + + def test_proxies_check_files(self): + self.styleguide.check_files(['foo', 'bar']) + + self.mocked_sg.check_files.assert_called_once_with( + paths=['foo', 'bar'] + ) + + def test_proxies_input_file(self): + self.styleguide.input_file('file.py', + lines=[9, 10], + expected='foo', + line_offset=20) + + self.mocked_sg.input_file.assert_called_once_with(filename='file.py', + lines=[9, 10], + expected='foo', + line_offset=20) + + def test_check_files_retries_on_specific_OSErrors(self): + self.mocked_sg.check_files.side_effect = oserror_generator( + errno.ENOSPC, 'No space left on device' + ) + + self.styleguide.check_files(['foo', 'bar']) + + self.mocked_sg.init_report.assert_called_once_with(pep8.StandardReport) + + def test_input_file_retries_on_specific_OSErrors(self): + self.mocked_sg.input_file.side_effect = oserror_generator( + errno.ENOSPC, 'No space left on device' + ) + + self.styleguide.input_file('file.py') + + self.mocked_sg.init_report.assert_called_once_with(pep8.StandardReport) + + def test_check_files_reraises_unknown_OSErrors(self): + self.mocked_sg.check_files.side_effect = oserror_generator( + errno.EADDRINUSE, + 'lol why are we talking about binding to sockets' + ) + + self.assertRaises(OSError, self.styleguide.check_files, + ['foo', 'bar']) + + def test_input_file_reraises_unknown_OSErrors(self): + self.mocked_sg.input_file.side_effect = oserror_generator( + errno.EADDRINUSE, + 'lol why are we talking about binding to sockets' + ) + + self.assertRaises(OSError, self.styleguide.input_file, + ['foo', 'bar']) + if __name__ == '__main__': unittest.main()