From 3bd8a21f62db68074289960b1a2ca5bc0b0e2308 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Tue, 1 Jan 2013 23:09:33 -0500 Subject: [PATCH] Split up flake8/run.py I've preserved the API for users expecting their hooks and other things to work. That said, I'm working on #51 now as well. --- flake8/hooks.py | 80 ++++++++++++++ flake8/main.py | 169 +++++++++++++++++++++++++++++ flake8/run.py | 274 +----------------------------------------------- flake8/util.py | 17 +++ 4 files changed, 270 insertions(+), 270 deletions(-) create mode 100644 flake8/hooks.py create mode 100644 flake8/main.py diff --git a/flake8/hooks.py b/flake8/hooks.py new file mode 100644 index 0000000..970fffa --- /dev/null +++ b/flake8/hooks.py @@ -0,0 +1,80 @@ +import os +from flake8.util import _initpep8, pep8style, skip_file +from subprocess import Popen, PIPE + + +def git_hook(complexity=-1, strict=False, ignore=None, lazy=False): + from flake8.main import check_file + _initpep8() + if ignore: + pep8style.options.ignore = ignore + + warnings = 0 + + gitcmd = "git diff-index --cached --name-only HEAD" + if lazy: + gitcmd = gitcmd.replace('--cached ', '') + + _, files_modified, _ = run(gitcmd) + for filename in files_modified: + ext = os.path.splitext(filename)[-1] + if ext != '.py': + continue + if not os.path.exists(filename): + continue + warnings += check_file(path=filename, ignore=ignore, + complexity=complexity) + + if strict: + return warnings + + return 0 + + +def hg_hook(ui, repo, **kwargs): + from flake8.main import check_file + _initpep8() + complexity = ui.config('flake8', 'complexity', default=-1) + warnings = 0 + + for file_ in _get_files(repo, **kwargs): + warnings += check_file(file_, complexity) + + strict = ui.configbool('flake8', 'strict', default=True) + + if strict: + return warnings + + return 0 + + +def run(command): + p = Popen(command.split(), stdout=PIPE, stderr=PIPE) + p.wait() + return (p.returncode, [line.strip() for line in p.stdout.readlines()], + [line.strip() for line in p.stderr.readlines()]) + + +def _get_files(repo, **kwargs): + seen = set() + for rev in range(repo[kwargs['node']], len(repo)): + for file_ in repo[rev].files(): + file_ = os.path.join(repo.root, file_) + if file_ in seen or not os.path.exists(file_): + continue + seen.add(file_) + if not file_.endswith('.py'): + continue + if skip_file(file_): + continue + yield file_ + + +def find_vcs(): + if os.path.isdir('.git'): + if not os.path.isdir('.git/hooks'): + os.mkdir('.git/hooks') + return '.git/hooks/pre-commit' + elif os.path.isdir('.hg'): + return '.hg/hgrc' + return '' diff --git a/flake8/main.py b/flake8/main.py new file mode 100644 index 0000000..df92450 --- /dev/null +++ b/flake8/main.py @@ -0,0 +1,169 @@ +import os +import sys +import pep8 +import flakey +import select +import optparse +from flake8 import mccabe +from flake8 import __version__ +from flake8.util import _initpep8, skip_file + +pep8style = None + + +def get_parser(): + """Create a custom OptionParser""" + + def version(option, opt, value, parser): + parser.print_usage() + parser.print_version() + sys.exit(0) + + # Create our own parser + parser = optparse.OptionParser('%prog [options] [file.py|directory]', + version=version) + parser.version = '{0} (pep8: {1}, flakey: {2})'.format( + __version__, pep8.__version__, flakey.__version__) + parser.remove_option('--version') + # don't overlap with pep8's verbose option + parser.add_option('--builtins', default='', dest='builtins', + help="append builtin functions to flakey's " + "_MAGIC_BUILTINS") + parser.add_option('--ignore', default='', + help='skip errors and warnings (e.g. E4,W)') + parser.add_option('--exit-zero', action='store_true', default=False, + help='Exit with status 0 even if there are errors') + parser.add_option('--max-complexity', default=-1, action='store', + type='int', help='McCabe complexity threshold') + parser.add_option('-V', '--version', action='callback', + callback=version, + help='Print the version info for flake8') + return parser + + +def main(): + global pep8style + + # parse out our flags so pep8 doesn't get confused + parser = get_parser() + opts, sys.argv = parser.parse_args() + + # make sure pep8 gets the information it expects + sys.argv.insert(0, 'pep8') + + pep8style = pep8.StyleGuide(parse_argv=True, config_file=True) + options = pep8style.options + complexity = opts.max_complexity + builtins = set(opts.builtins.split(',')) + warnings = 0 + stdin = None + + if builtins: + orig_builtins = set(flakey.checker._MAGIC_GLOBALS) + flakey.checker._MAGIC_GLOBALS = orig_builtins | builtins + + if pep8style.paths and options.filename is not None: + for path in _get_python_files(pep8style.paths): + if path == '-': + if stdin is None: + stdin = read_stdin() + warnings += check_code(stdin, opts.ignore, complexity) + else: + warnings += check_file(path, opts.ignore, complexity) + else: + stdin = read_stdin() + warnings += check_code(stdin, opts.ignore, complexity) + + if opts.exit_zero: + raise SystemExit(0) + + raise SystemExit(warnings) + + +def check_file(path, ignore=(), complexity=-1): + if pep8style.excluded(path): + return 0 + warning = flakey.checkPath(path) + warnings = flakey.print_messages(warning, ignore=ignore) + warnings += pep8style.input_file(path) + if complexity > -1: + warnings += mccabe.get_module_complexity(path, complexity) + return warnings + + +def check_code(code, ignore=(), complexity=-1): + warning = flakey.check(code, 'stdin') + warnings = flakey.print_messages(warning, ignore=ignore) + warnings += pep8style.input_file(None, lines=code.split('\n')) + if complexity > -1: + warnings += mccabe.get_code_complexity(code, complexity) + return warnings + + +def read_stdin(): + # wait for 1 second on the stdin fd + reads, __, __ = select.select([sys.stdin], [], [], 1.) + if reads == []: + print('input not specified') + raise SystemExit(1) + + return sys.stdin.read() + + +try: + from setuptools import Command +except ImportError: + Flake8Command = None +else: + class Flake8Command(Command): + description = "Run flake8 on modules registered in setuptools" + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def distribution_files(self): + if self.distribution.packages: + for package in self.distribution.packages: + yield package.replace(".", os.path.sep) + + if self.distribution.py_modules: + for filename in self.distribution.py_modules: + yield "%s.py" % filename + + def run(self): + _initpep8() + + # _get_python_files can produce the same file several + # times, if one of its paths is a parent of another. Keep + # a set of checked files to de-duplicate. + checked = set() + + warnings = 0 + for path in _get_python_files(self.distribution_files()): + if path not in checked: + warnings += check_file(path) + checked.add(path) + + raise SystemExit(warnings > 0) + + +def _get_python_files(paths): + for path in paths: + if os.path.isdir(path): + for dirpath, dirnames, filenames in os.walk(path): + if pep8style.excluded(dirpath): + continue + for filename in filenames: + if not filename.endswith('.py'): + continue + fullpath = os.path.join(dirpath, filename) + if not skip_file(fullpath) or pep8style.excluded(fullpath): + yield fullpath + + else: + if not skip_file(path) or pep8style.excluded(path): + yield path diff --git a/flake8/run.py b/flake8/run.py index 858aaa3..e574c39 100644 --- a/flake8/run.py +++ b/flake8/run.py @@ -2,276 +2,10 @@ """ Implementation of the command-line I{flake8} tool. """ -import sys -import os -import os.path -import optparse -from subprocess import PIPE, Popen -import select - -from flake8 import mccabe -from flake8.util import skip_file -from flake8 import __version__ -import pep8 -import flakey - -pep8style = None - - -def check_file(path, ignore=(), complexity=-1): - if pep8style.excluded(path): - return 0 - warning = flakey.checkPath(path) - warnings = flakey.print_messages(warning, ignore=ignore) - warnings += pep8style.input_file(path) - if complexity > -1: - warnings += mccabe.get_module_complexity(path, complexity) - return warnings - - -def check_code(code, ignore=(), complexity=-1): - warning = flakey.check(code, 'stdin') - warnings = flakey.print_messages(warning, ignore=ignore) - warnings += pep8style.input_file(None, lines=code.split('\n')) - if complexity > -1: - warnings += mccabe.get_code_complexity(code, complexity) - return warnings - - -def _get_python_files(paths): - for path in paths: - if os.path.isdir(path): - for dirpath, dirnames, filenames in os.walk(path): - if pep8style.excluded(dirpath): - continue - for filename in filenames: - if not filename.endswith('.py'): - continue - fullpath = os.path.join(dirpath, filename) - if not skip_file(fullpath) or pep8style.excluded(fullpath): - yield fullpath - - else: - if not skip_file(path) or pep8style.excluded(path): - yield path - - -def read_stdin(): - # wait for 1 second on the stdin fd - reads, __, __ = select.select([sys.stdin], [], [], 1.) - if reads == []: - print('input not specified') - raise SystemExit(1) - - return sys.stdin.read() - - -def get_parser(): - """Create a custom OptionParser""" - - def version(option, opt, value, parser): - parser.print_usage() - parser.print_version() - sys.exit(0) - - # Create our own parser - parser = optparse.OptionParser('%prog [options] [file.py|directory]', - version=version) - parser.version = '{0} (pep8: {1}, flakey: {2})'.format( - __version__, pep8.__version__, flakey.__version__) - parser.remove_option('--version') - # don't overlap with pep8's verbose option - parser.add_option('--builtins', default='', dest='builtins', - help="append builtin functions to flakey's " - "_MAGIC_BUILTINS") - parser.add_option('--ignore', default='', - help='skip errors and warnings (e.g. E4,W)') - parser.add_option('--exit-zero', action='store_true', default=False, - help='Exit with status 0 even if there are errors') - parser.add_option('--max-complexity', default=-1, action='store', - type='int', help='McCabe complexity threshold') - parser.add_option('-V', '--version', action='callback', - callback=version, - help='Print the version info for flake8') - return parser - - -def main(): - global pep8style - - # parse out our flags so pep8 doesn't get confused - parser = get_parser() - opts, sys.argv = parser.parse_args() - - # make sure pep8 gets the information it expects - sys.argv.insert(0, 'pep8') - - pep8style = pep8.StyleGuide(parse_argv=True, config_file=True) - options = pep8style.options - complexity = opts.max_complexity - builtins = set(opts.builtins.split(',')) - warnings = 0 - stdin = None - - if builtins: - orig_builtins = set(flakey.checker._MAGIC_GLOBALS) - flakey.checker._MAGIC_GLOBALS = orig_builtins | builtins - - if pep8style.paths and options.filename is not None: - for path in _get_python_files(pep8style.paths): - if path == '-': - if stdin is None: - stdin = read_stdin() - warnings += check_code(stdin, opts.ignore, complexity) - else: - warnings += check_file(path, opts.ignore, complexity) - else: - stdin = read_stdin() - warnings += check_code(stdin, opts.ignore, complexity) - - if opts.exit_zero: - raise SystemExit(0) - - raise SystemExit(warnings) - - -def _get_files(repo, **kwargs): - seen = set() - for rev in range(repo[kwargs['node']], len(repo)): - for file_ in repo[rev].files(): - file_ = os.path.join(repo.root, file_) - if file_ in seen or not os.path.exists(file_): - continue - seen.add(file_) - if not file_.endswith('.py'): - continue - if skip_file(file_): - continue - yield file_ - - -class _PEP8Options(object): - # Default options taken from pep8.process_options() - max_complexity = -1 - verbose = False - quiet = False - no_repeat = False - exclude = [exc.rstrip('/') for exc in pep8.DEFAULT_EXCLUDE.split(',')] - filename = ['*.py'] - select = [] - ignore = pep8.DEFAULT_IGNORE.split(',') # or []? - show_source = False - show_pep8 = False - statistics = False - count = False - benchmark = False - testsuite = '' - doctest = False - max_line_length = pep8.MAX_LINE_LENGTH - - -def _initpep8(): - # default pep8 setup - global pep8style - if pep8style is None: - pep8style = pep8.StyleGuide(config_file=True) - pep8style.options.physical_checks = pep8.find_checks('physical_line') - pep8style.options.logical_checks = pep8.find_checks('logical_line') - pep8style.options.counters = dict.fromkeys(pep8.BENCHMARK_KEYS, 0) - pep8style.options.messages = {} - pep8style.options.max_line_length = 79 - pep8style.args = [] - - -def run(command): - p = Popen(command.split(), stdout=PIPE, stderr=PIPE) - p.wait() - return (p.returncode, [line.strip() for line in p.stdout.readlines()], - [line.strip() for line in p.stderr.readlines()]) - - -def git_hook(complexity=-1, strict=False, ignore=None, lazy=False): - _initpep8() - if ignore: - pep8style.options.ignore = ignore - - warnings = 0 - - gitcmd = "git diff-index --cached --name-only HEAD" - if lazy: - gitcmd = gitcmd.replace('--cached ', '') - - _, files_modified, _ = run(gitcmd) - for filename in files_modified: - ext = os.path.splitext(filename)[-1] - if ext != '.py': - continue - if not os.path.exists(filename): - continue - warnings += check_file(path=filename, ignore=ignore, - complexity=complexity) - - if strict: - return warnings - - return 0 - - -def hg_hook(ui, repo, **kwargs): - _initpep8() - complexity = ui.config('flake8', 'complexity', default=-1) - warnings = 0 - - for file_ in _get_files(repo, **kwargs): - warnings += check_file(file_, complexity) - - strict = ui.configbool('flake8', 'strict', default=True) - - if strict: - return warnings - - return 0 - - -try: - from setuptools import Command -except ImportError: - Flake8Command = None -else: - class Flake8Command(Command): - description = "Run flake8 on modules registered in setuptools" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def distribution_files(self): - if self.distribution.packages: - for package in self.distribution.packages: - yield package.replace(".", os.path.sep) - - if self.distribution.py_modules: - for filename in self.distribution.py_modules: - yield "%s.py" % filename - - def run(self): - _initpep8() - - # _get_python_files can produce the same file several - # times, if one of its paths is a parent of another. Keep - # a set of checked files to de-duplicate. - checked = set() - - warnings = 0 - for path in _get_python_files(self.distribution_files()): - if path not in checked: - warnings += check_file(path) - checked.add(path) - - raise SystemExit(warnings > 0) +from flake8.hooks import git_hook, hg_hook # noqa +from flake8.main import main +from flake8.main import (check_code, check_file, get_parser, # noqa + Flake8Command) # noqa if __name__ == '__main__': diff --git a/flake8/util.py b/flake8/util.py index c9a6a75..1323857 100644 --- a/flake8/util.py +++ b/flake8/util.py @@ -2,6 +2,8 @@ from __future__ import with_statement import re import os +pep8style = None + def skip_warning(warning, ignore=[]): # XXX quick dirty hack, just need to keep the line in the warning @@ -40,3 +42,18 @@ def skip_file(path): finally: f.close() return _NOQA.search(content) is not None + + +def _initpep8(): + # default pep8 setup + global pep8style + import pep8 + if pep8style is None: + pep8style = pep8.StyleGuide(config_file=True) + pep8style.options.physical_checks = pep8.find_checks('physical_line') + pep8style.options.logical_checks = pep8.find_checks('logical_line') + pep8style.options.counters = dict.fromkeys(pep8.BENCHMARK_KEYS, 0) + pep8style.options.messages = {} + pep8style.options.max_line_length = 79 + pep8style.args = [] + return pep8style