diff --git a/flake8/_pyflakes.py b/flake8/_pyflakes.py index 12a8344..976b2ab 100644 --- a/flake8/_pyflakes.py +++ b/flake8/_pyflakes.py @@ -6,6 +6,9 @@ except ImportError: pass else: demandimport.disable() +import os + +import pep8 import pyflakes import pyflakes.checker @@ -39,8 +42,24 @@ class FlakesChecker(pyflakes.checker.Checker): version = pyflakes.__version__ def __init__(self, tree, filename): + filename = pep8.normalize_paths(filename)[0] + withDoctest = self.withDoctest + included_by = [include for include in self.include_in_doctest + if include != '' and filename.startswith(include)] + if included_by: + withDoctest = True + + for exclude in self.exclude_from_doctest: + if exclude != '' and filename.startswith(exclude): + withDoctest = False + overlaped_by = [include for include in included_by + if include.startswith(exclude)] + + if overlaped_by: + withDoctest = True + super(FlakesChecker, self).__init__(tree, filename, - withDoctest=self.withDoctest) + withDoctest=withDoctest) @classmethod def add_options(cls, parser): @@ -48,7 +67,17 @@ class FlakesChecker(pyflakes.checker.Checker): help="define more built-ins, comma separated") parser.add_option('--doctests', default=False, action='store_true', help="check syntax of the doctests") - parser.config_options.extend(['builtins', 'doctests']) + parser.add_option('--include-in-doctest', default='', + dest='include_in_doctest', + help='Run doctests only on these files', + type='string') + parser.add_option('--exclude-from-doctest', default='', + dest='exclude_from_doctest', + help='Skip these files when running doctests', + type='string') + parser.config_options.extend(['builtins', 'doctests', + 'include-in-doctest', + 'exclude-from-doctest']) @classmethod def parse_options(cls, options): @@ -56,6 +85,35 @@ class FlakesChecker(pyflakes.checker.Checker): cls.builtIns = cls.builtIns.union(options.builtins.split(',')) cls.withDoctest = options.doctests + included_files = [] + for included_file in options.include_in_doctest.split(','): + if included_file == '': + continue + if not included_file.startswith((os.sep, './', '~/')): + included_files.append('./' + included_file) + else: + included_files.append(included_file) + cls.include_in_doctest = pep8.normalize_paths(','.join(included_files)) + + excluded_files = [] + for excluded_file in options.exclude_from_doctest.split(','): + if excluded_file == '': + continue + if not excluded_file.startswith((os.sep, './', '~/')): + excluded_files.append('./' + excluded_file) + else: + excluded_files.append(excluded_file) + cls.exclude_from_doctest = pep8.normalize_paths( + ','.join(excluded_files)) + + inc_exc = set(cls.include_in_doctest).intersection( + set(cls.exclude_from_doctest)) + if inc_exc: + raise ValueError('"%s" was specified in both the ' + 'include-in-doctest and exclude-from-doctest ' + 'options. You are not allowed to specify it in ' + 'both for doctesting.' % inc_exc) + def run(self): for m in self.messages: col = getattr(m, 'col', 0) diff --git a/flake8/engine.py b/flake8/engine.py index d929f83..539571a 100644 --- a/flake8/engine.py +++ b/flake8/engine.py @@ -232,7 +232,6 @@ def get_style_guide(**kwargs): kwargs['parser'], options_hooks = get_parser() styleguide = StyleGuide(**kwargs) options = styleguide.options - _disable_extensions(kwargs['parser'], options) if options.exclude and not isinstance(options.exclude, list): diff --git a/flake8/tests/test_pyflakes.py b/flake8/tests/test_pyflakes.py new file mode 100644 index 0000000..b5722c6 --- /dev/null +++ b/flake8/tests/test_pyflakes.py @@ -0,0 +1,74 @@ +from __future__ import with_statement + +import ast +import unittest + +from collections import namedtuple + +from flake8._pyflakes import FlakesChecker + +Options = namedtuple("Options", ['builtins', 'doctests', + 'include_in_doctest', + 'exclude_from_doctest']) + + +class TestFlakesChecker(unittest.TestCase): + + def setUp(self): + self.tree = ast.parse('print("cookies")') + + def test_doctest_flag_enabled(self): + options = Options(builtins=None, doctests=True, + include_in_doctest='', + exclude_from_doctest='') + FlakesChecker.parse_options(options) + flake_checker = FlakesChecker(self.tree, 'cookies.txt') + assert flake_checker.withDoctest is True + + def test_doctest_flag_disabled(self): + options = Options(builtins=None, doctests=False, + include_in_doctest='', + exclude_from_doctest='') + FlakesChecker.parse_options(options) + flake_checker = FlakesChecker(self.tree, 'cookies.txt') + assert flake_checker.withDoctest is False + + def test_doctest_flag_enabled_exclude_file(self): + options = Options(builtins=None, doctests=True, + include_in_doctest='', + exclude_from_doctest='cookies.txt,' + 'hungry/cookies.txt') + FlakesChecker.parse_options(options) + flake_checker = FlakesChecker(self.tree, './cookies.txt') + assert flake_checker.withDoctest is False + + def test_doctest_flag_disabled_include_file(self): + options = Options(builtins=None, doctests=False, + include_in_doctest='./cookies.txt,cake_yuck.txt', + exclude_from_doctest='') + FlakesChecker.parse_options(options) + flake_checker = FlakesChecker(self.tree, './cookies.txt') + assert flake_checker.withDoctest is True + + def test_doctest_flag_disabled_include_file_exclude_dir(self): + options = Options(builtins=None, doctests=False, + include_in_doctest='./cookies.txt', + exclude_from_doctest='./') + FlakesChecker.parse_options(options) + flake_checker = FlakesChecker(self.tree, './cookies.txt') + assert flake_checker.withDoctest is True + + def test_doctest_flag_disabled_include_dir_exclude_file(self): + options = Options(builtins=None, doctests=False, + include_in_doctest='./', + exclude_from_doctest='./cookies.txt') + FlakesChecker.parse_options(options) + flake_checker = FlakesChecker(self.tree, './cookies.txt') + assert flake_checker.withDoctest is False + + def test_doctest_flag_disabled_include_file_exclude_file_error(self): + options = Options(builtins=None, doctests=False, + include_in_doctest='./cookies.txt', + exclude_from_doctest='./cookies.txt,cake_yuck.txt') + with self.assertRaises(ValueError): + FlakesChecker.parse_options(options)