# -*- 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, 17) 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, 17) 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