"""Plugin built-in to Flake8 to treat pyflakes as a plugin.""" # -*- coding: utf-8 -*- from __future__ import absolute_import try: # The 'demandimport' breaks pyflakes and flake8.plugins.pyflakes from mercurial import demandimport except ImportError: pass else: demandimport.disable() import os import pyflakes import pyflakes.checker from flake8 import utils FLAKE8_PYFLAKES_CODES = { 'UnusedImport': 'F401', 'ImportShadowedByLoopVar': 'F402', 'ImportStarUsed': 'F403', 'LateFutureImport': 'F404', 'ImportStarUsage': 'F405', 'ImportStarNotPermitted': 'F406', 'FutureFeatureNotDefined': 'F407', 'MultiValueRepeatedKeyLiteral': 'F601', 'MultiValueRepeatedKeyVariable': 'F602', 'TooManyExpressionsInStarredAssignment': 'F621', 'TwoStarredExpressions': 'F622', 'AssertTuple': 'F631', 'BreakOutsideLoop': 'F701', 'ContinueOutsideLoop': 'F702', 'ContinueInFinally': 'F703', 'YieldOutsideFunction': 'F704', 'ReturnWithArgsInsideGenerator': 'F705', 'ReturnOutsideFunction': 'F706', 'DefaultExceptNotLast': 'F707', 'DoctestSyntaxError': 'F721', 'RedefinedWhileUnused': 'F811', 'RedefinedInListComp': 'F812', 'UndefinedName': 'F821', 'UndefinedExport': 'F822', 'UndefinedLocal': 'F823', 'DuplicateArgument': 'F831', 'UnusedVariable': 'F841', } def patch_pyflakes(): """Add error codes to Pyflakes messages.""" for name, obj in vars(pyflakes.messages).items(): if name[0].isupper() and obj.message: obj.flake8_msg = '%s %s' % ( FLAKE8_PYFLAKES_CODES.get(name, 'F999'), obj.message ) patch_pyflakes() class FlakesChecker(pyflakes.checker.Checker): """Subclass the Pyflakes checker to conform with the flake8 API.""" name = 'pyflakes' version = pyflakes.__version__ with_doctest = False include_in_doctest = [] exclude_from_doctest = [] def __init__(self, tree, filename): """Initialize the PyFlakes plugin with an AST tree and filename.""" filename = utils.normalize_paths(filename)[0] with_doctest = self.with_doctest included_by = [include for include in self.include_in_doctest if include != '' and filename.startswith(include)] if included_by: with_doctest = True for exclude in self.exclude_from_doctest: if exclude != '' and filename.startswith(exclude): with_doctest = False overlaped_by = [include for include in included_by if include.startswith(exclude)] if overlaped_by: with_doctest = True super(FlakesChecker, self).__init__(tree, filename, withDoctest=with_doctest) @classmethod def add_options(cls, parser): """Register options for PyFlakes on the Flake8 OptionManager.""" parser.add_option( '--builtins', parse_from_config=True, comma_separated_list=True, help="define more built-ins, comma separated", ) parser.add_option( '--doctests', default=False, action='store_true', parse_from_config=True, help="check syntax of the doctests", ) parser.add_option( '--include-in-doctest', default='', dest='include_in_doctest', parse_from_config=True, comma_separated_list=True, normalize_paths=True, help='Run doctests only on these files', type='string', ) parser.add_option( '--exclude-from-doctest', default='', dest='exclude_from_doctest', parse_from_config=True, comma_separated_list=True, normalize_paths=True, help='Skip these files when running doctests', type='string', ) @classmethod def parse_options(cls, options): """Parse option values from Flake8's OptionManager.""" if options.builtins: cls.builtIns = cls.builtIns.union(options.builtins) cls.with_doctest = options.doctests included_files = [] for included_file in options.include_in_doctest: 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 = utils.normalize_paths(included_files) excluded_files = [] for excluded_file in options.exclude_from_doctest: 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 = utils.normalize_paths(excluded_files) inc_exc = set(cls.include_in_doctest).intersection( 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): """Run the plugin.""" for message in self.messages: col = getattr(message, 'col', 0) yield (message.lineno, col, (message.flake8_msg % message.message_args), message.__class__)