| # -*- coding: utf-8 -*- |
| import os.path |
| import shlex |
| import sys |
| import unittest |
| |
| import pycodestyle |
| from testsuite.support import ROOT_DIR, PseudoFile |
| |
| E11 = os.path.join(ROOT_DIR, 'testsuite', 'E11.py') |
| |
| |
| class DummyChecker(object): |
| def __init__(self, tree, filename): |
| pass |
| |
| def run(self): |
| if False: |
| yield |
| |
| |
| class APITestCase(unittest.TestCase): |
| """Test the public methods.""" |
| |
| def setUp(self): |
| self._saved_stdout = sys.stdout |
| self._saved_stderr = sys.stderr |
| self._saved_checks = pycodestyle._checks |
| sys.stdout = PseudoFile() |
| sys.stderr = PseudoFile() |
| pycodestyle._checks = { |
| k: {f: (vals[0][:], vals[1]) for (f, vals) in v.items()} |
| for k, v in self._saved_checks.items() |
| } |
| |
| def tearDown(self): |
| sys.stdout = self._saved_stdout |
| sys.stderr = self._saved_stderr |
| pycodestyle._checks = self._saved_checks |
| |
| def reset(self): |
| del sys.stdout[:], sys.stderr[:] |
| |
| def test_register_physical_check(self): |
| def check_dummy(physical_line, line_number): |
| if False: |
| yield |
| pycodestyle.register_check(check_dummy, ['Z001']) |
| |
| self.assertTrue(check_dummy in pycodestyle._checks['physical_line']) |
| codes, args = pycodestyle._checks['physical_line'][check_dummy] |
| self.assertTrue('Z001' in codes) |
| self.assertEqual(args, ['physical_line', 'line_number']) |
| |
| options = pycodestyle.StyleGuide().options |
| self.assertTrue(any(func == check_dummy |
| for name, func, args in options.physical_checks)) |
| |
| def test_register_logical_check(self): |
| def check_dummy(logical_line, tokens): |
| if False: |
| yield |
| pycodestyle.register_check(check_dummy, ['Z401']) |
| |
| self.assertTrue(check_dummy in pycodestyle._checks['logical_line']) |
| codes, args = pycodestyle._checks['logical_line'][check_dummy] |
| self.assertTrue('Z401' in codes) |
| self.assertEqual(args, ['logical_line', 'tokens']) |
| |
| pycodestyle.register_check(check_dummy, []) |
| pycodestyle.register_check(check_dummy, ['Z402', 'Z403']) |
| codes, args = pycodestyle._checks['logical_line'][check_dummy] |
| self.assertEqual(codes, ['Z401', 'Z402', 'Z403']) |
| self.assertEqual(args, ['logical_line', 'tokens']) |
| |
| options = pycodestyle.StyleGuide().options |
| self.assertTrue(any(func == check_dummy |
| for name, func, args in options.logical_checks)) |
| |
| def test_register_ast_check(self): |
| pycodestyle.register_check(DummyChecker, ['Z701']) |
| |
| self.assertTrue(DummyChecker in pycodestyle._checks['tree']) |
| codes, args = pycodestyle._checks['tree'][DummyChecker] |
| self.assertTrue('Z701' in codes) |
| self.assertTrue(args is None) |
| |
| options = pycodestyle.StyleGuide().options |
| self.assertTrue(any(cls == DummyChecker |
| for name, cls, args in options.ast_checks)) |
| |
| def test_register_invalid_check(self): |
| class InvalidChecker(DummyChecker): |
| def __init__(self, filename): |
| pass |
| |
| def check_dummy(logical, tokens): |
| if False: |
| yield |
| pycodestyle.register_check(InvalidChecker, ['Z741']) |
| pycodestyle.register_check(check_dummy, ['Z441']) |
| |
| for checkers in pycodestyle._checks.values(): |
| self.assertTrue(DummyChecker not in checkers) |
| self.assertTrue(check_dummy not in checkers) |
| |
| self.assertRaises(TypeError, pycodestyle.register_check) |
| |
| def test_styleguide(self): |
| report = pycodestyle.StyleGuide().check_files() |
| self.assertEqual(report.total_errors, 0) |
| self.assertFalse(sys.stdout) |
| self.assertFalse(sys.stderr) |
| self.reset() |
| |
| report = pycodestyle.StyleGuide().check_files(['missing-file']) |
| stdout = sys.stdout.getvalue().splitlines() |
| self.assertEqual(len(stdout), report.total_errors) |
| self.assertEqual(report.total_errors, 1) |
| # < 3.3 returns IOError; >= 3.3 returns FileNotFoundError |
| self.assertTrue(stdout[0].startswith("missing-file:1:1: E902 ")) |
| self.assertFalse(sys.stderr) |
| self.reset() |
| |
| report = pycodestyle.StyleGuide().check_files([E11]) |
| stdout = sys.stdout.getvalue().splitlines() |
| self.assertEqual(len(stdout), report.total_errors) |
| self.assertEqual(report.total_errors, 24) |
| self.assertFalse(sys.stderr) |
| self.reset() |
| |
| # Passing the paths in the constructor gives same result |
| report = pycodestyle.StyleGuide(paths=[E11]).check_files() |
| stdout = sys.stdout.getvalue().splitlines() |
| self.assertEqual(len(stdout), report.total_errors) |
| self.assertEqual(report.total_errors, 24) |
| self.assertFalse(sys.stderr) |
| self.reset() |
| |
| def test_styleguide_options(self): |
| # Instantiate a simple checker |
| pep8style = pycodestyle.StyleGuide(paths=[E11]) |
| |
| # Check style's attributes |
| self.assertEqual(pep8style.checker_class, pycodestyle.Checker) |
| self.assertEqual(pep8style.paths, [E11]) |
| self.assertEqual(pep8style.runner, pep8style.input_file) |
| self.assertEqual(pep8style.options.ignore_code, pep8style.ignore_code) |
| self.assertEqual(pep8style.options.paths, pep8style.paths) |
| |
| # Check unset options |
| for o in ('benchmark', 'config', 'count', 'diff', |
| 'doctest', 'quiet', 'show_pep8', 'show_source', |
| 'statistics', 'testsuite', 'verbose'): |
| oval = getattr(pep8style.options, o) |
| self.assertTrue(oval in (None, False), msg='%s = %r' % (o, oval)) |
| |
| # Check default options |
| self.assertTrue(pep8style.options.repeat) |
| self.assertEqual(pep8style.options.benchmark_keys, |
| ['directories', 'files', |
| 'logical lines', 'physical lines']) |
| self.assertEqual(pep8style.options.exclude, |
| ['.svn', 'CVS', '.bzr', '.hg', |
| '.git', '__pycache__', '.tox']) |
| self.assertEqual(pep8style.options.filename, ['*.py']) |
| self.assertEqual(pep8style.options.format, 'default') |
| self.assertEqual(pep8style.options.select, ()) |
| self.assertEqual(pep8style.options.ignore, ('E226', 'E24', 'W504')) |
| self.assertEqual(pep8style.options.max_line_length, 79) |
| |
| def test_styleguide_ignore_code(self): |
| def parse_argv(argstring): |
| _saved_argv = sys.argv |
| sys.argv = shlex.split('pycodestyle %s /dev/null' % argstring) |
| try: |
| return pycodestyle.StyleGuide(parse_argv=True) |
| finally: |
| sys.argv = _saved_argv |
| |
| options = parse_argv('').options |
| self.assertEqual(options.select, ()) |
| self.assertEqual( |
| options.ignore, |
| ('E121', 'E123', 'E126', 'E226', 'E24', 'E704', 'W503', 'W504') |
| ) |
| |
| options = parse_argv('--doctest').options |
| self.assertEqual(options.select, ()) |
| self.assertEqual(options.ignore, ()) |
| |
| options = parse_argv('--ignore E,W').options |
| self.assertEqual(options.select, ()) |
| self.assertEqual(options.ignore, ('E', 'W')) |
| |
| options = parse_argv('--select E,W').options |
| self.assertEqual(options.select, ('E', 'W')) |
| self.assertEqual(options.ignore, ('',)) |
| |
| options = parse_argv('--select E --ignore E24').options |
| self.assertEqual(options.select, ('E',)) |
| self.assertEqual(options.ignore, ('',)) |
| |
| options = parse_argv('--ignore E --select E24').options |
| self.assertEqual(options.select, ('E24',)) |
| self.assertEqual(options.ignore, ('',)) |
| |
| options = parse_argv('--ignore W --select E24').options |
| self.assertEqual(options.select, ('E24',)) |
| self.assertEqual(options.ignore, ('',)) |
| |
| options = parse_argv('--max-doc-length=72').options |
| self.assertEqual(options.max_doc_length, 72) |
| |
| options = parse_argv('').options |
| self.assertEqual(options.max_doc_length, None) |
| |
| pep8style = pycodestyle.StyleGuide(paths=[E11]) |
| self.assertFalse(pep8style.ignore_code('E112')) |
| self.assertFalse(pep8style.ignore_code('W191')) |
| self.assertTrue(pep8style.ignore_code('E241')) |
| |
| pep8style = pycodestyle.StyleGuide(select='E', paths=[E11]) |
| self.assertFalse(pep8style.ignore_code('E112')) |
| self.assertTrue(pep8style.ignore_code('W191')) |
| self.assertFalse(pep8style.ignore_code('E241')) |
| |
| pep8style = pycodestyle.StyleGuide(select='W', paths=[E11]) |
| self.assertTrue(pep8style.ignore_code('E112')) |
| self.assertFalse(pep8style.ignore_code('W191')) |
| self.assertTrue(pep8style.ignore_code('E241')) |
| |
| pep8style = pycodestyle.StyleGuide(select=('F401',), paths=[E11]) |
| self.assertEqual(pep8style.options.select, ('F401',)) |
| self.assertEqual(pep8style.options.ignore, ('',)) |
| self.assertFalse(pep8style.ignore_code('F')) |
| self.assertFalse(pep8style.ignore_code('F401')) |
| self.assertTrue(pep8style.ignore_code('F402')) |
| |
| def test_styleguide_excluded(self): |
| pep8style = pycodestyle.StyleGuide(paths=[E11]) |
| |
| self.assertFalse(pep8style.excluded('./foo/bar')) |
| self.assertFalse(pep8style.excluded('./foo/bar/main.py')) |
| |
| self.assertTrue(pep8style.excluded('./CVS')) |
| self.assertTrue(pep8style.excluded('./.tox')) |
| self.assertTrue(pep8style.excluded('./subdir/CVS')) |
| self.assertTrue(pep8style.excluded('__pycache__')) |
| self.assertTrue(pep8style.excluded('./__pycache__')) |
| self.assertTrue(pep8style.excluded('subdir/__pycache__')) |
| |
| self.assertFalse(pep8style.excluded('draftCVS')) |
| self.assertFalse(pep8style.excluded('./CVSoup')) |
| self.assertFalse(pep8style.excluded('./CVS/subdir')) |
| |
| def test_styleguide_checks(self): |
| pep8style = pycodestyle.StyleGuide(paths=[E11]) |
| |
| # Default lists of checkers |
| self.assertTrue(len(pep8style.options.physical_checks) > 4) |
| self.assertTrue(len(pep8style.options.logical_checks) > 10) |
| self.assertEqual(len(pep8style.options.ast_checks), 0) |
| |
| # Sanity check |
| for name, check, args in pep8style.options.physical_checks: |
| self.assertEqual(check.__name__, name) |
| self.assertEqual(args[0], 'physical_line') |
| for name, check, args in pep8style.options.logical_checks: |
| self.assertEqual(check.__name__, name) |
| self.assertEqual(args[0], 'logical_line') |
| |
| # Do run E11 checks |
| options = pycodestyle.StyleGuide().options |
| self.assertTrue(any(func == pycodestyle.indentation |
| for name, func, args in options.logical_checks)) |
| options = pycodestyle.StyleGuide(select=['E']).options |
| self.assertTrue(any(func == pycodestyle.indentation |
| for name, func, args in options.logical_checks)) |
| options = pycodestyle.StyleGuide(ignore=['W']).options |
| self.assertTrue(any(func == pycodestyle.indentation |
| for name, func, args in options.logical_checks)) |
| options = pycodestyle.StyleGuide(ignore=['E12']).options |
| self.assertTrue(any(func == pycodestyle.indentation |
| for name, func, args in options.logical_checks)) |
| |
| # Do not run E11 checks |
| options = pycodestyle.StyleGuide(select=['W']).options |
| self.assertFalse(any(func == pycodestyle.indentation |
| for name, func, args in options.logical_checks)) |
| options = pycodestyle.StyleGuide(ignore=['E']).options |
| self.assertFalse(any(func == pycodestyle.indentation |
| for name, func, args in options.logical_checks)) |
| options = pycodestyle.StyleGuide(ignore=['E11']).options |
| self.assertFalse(any(func == pycodestyle.indentation |
| for name, func, args in options.logical_checks)) |
| |
| def test_styleguide_init_report(self): |
| style = pycodestyle.StyleGuide(paths=[E11]) |
| |
| standard_report = pycodestyle.StandardReport |
| |
| self.assertEqual(style.options.reporter, standard_report) |
| self.assertEqual(type(style.options.report), standard_report) |
| |
| class MinorityReport(pycodestyle.BaseReport): |
| pass |
| |
| report = style.init_report(MinorityReport) |
| self.assertEqual(style.options.report, report) |
| self.assertEqual(type(report), MinorityReport) |
| |
| style = pycodestyle.StyleGuide(paths=[E11], reporter=MinorityReport) |
| self.assertEqual(type(style.options.report), MinorityReport) |
| self.assertEqual(style.options.reporter, MinorityReport) |
| |
| def test_styleguide_check_files(self): |
| pep8style = pycodestyle.StyleGuide(paths=[E11]) |
| |
| report = pep8style.check_files() |
| self.assertTrue(report.total_errors) |
| |
| self.assertRaises(TypeError, pep8style.check_files, 42) |
| # < 3.3 raises TypeError; >= 3.3 raises AttributeError |
| self.assertRaises(Exception, pep8style.check_files, [42]) |
| |
| def test_check_unicode(self): |
| # Do not crash if lines are Unicode (Python 2.x) |
| pycodestyle.register_check(DummyChecker, ['Z701']) |
| source = '#\n' |
| if hasattr(source, 'decode'): |
| source = source.decode('ascii') |
| |
| pep8style = pycodestyle.StyleGuide() |
| count_errors = pep8style.input_file('stdin', lines=[source]) |
| |
| self.assertFalse(sys.stdout) |
| self.assertFalse(sys.stderr) |
| self.assertEqual(count_errors, 0) |
| |
| def test_check_nullbytes(self): |
| pycodestyle.register_check(DummyChecker, ['Z701']) |
| |
| pep8style = pycodestyle.StyleGuide() |
| count_errors = pep8style.input_file('stdin', lines=['\x00\n']) |
| |
| stdout = sys.stdout.getvalue() |
| if 'SyntaxError' in stdout: |
| # PyPy 2.2 returns a SyntaxError |
| expected = "stdin:1:2: E901 SyntaxError" |
| elif 'ValueError' in stdout: |
| # Python 3.5. |
| expected = "stdin:1:1: E901 ValueError" |
| else: |
| expected = "stdin:1:1: E901 TypeError" |
| self.assertTrue(stdout.startswith(expected), |
| msg='Output %r does not start with %r' % |
| (stdout, expected)) |
| self.assertFalse(sys.stderr) |
| self.assertEqual(count_errors, 1) |
| |
| def test_styleguide_unmatched_triple_quotes(self): |
| pycodestyle.register_check(DummyChecker, ['Z701']) |
| lines = [ |
| 'def foo():\n', |
| ' """test docstring""\'\n', |
| ] |
| |
| pep8style = pycodestyle.StyleGuide() |
| pep8style.input_file('stdin', lines=lines) |
| stdout = sys.stdout.getvalue() |
| |
| expected = 'stdin:2:5: E901 TokenError: EOF in multi-line string' |
| self.assertTrue(expected in stdout) |
| |
| def test_styleguide_continuation_line_outdented(self): |
| pycodestyle.register_check(DummyChecker, ['Z701']) |
| lines = [ |
| 'def foo():\n', |
| ' pass\n', |
| '\n', |
| '\\\n', |
| '\n', |
| 'def bar():\n', |
| ' pass\n', |
| ] |
| |
| pep8style = pycodestyle.StyleGuide() |
| count_errors = pep8style.input_file('stdin', lines=lines) |
| self.assertEqual(count_errors, 2) |
| stdout = sys.stdout.getvalue() |
| expected = ( |
| 'stdin:6:1: ' |
| 'E122 continuation line missing indentation or outdented' |
| ) |
| self.assertTrue(expected in stdout) |
| expected = 'stdin:6:1: E302 expected 2 blank lines, found 1' |
| self.assertTrue(expected in stdout) |
| |
| # TODO: runner |
| # TODO: input_file |