From 3c4015719589cb372f0d9bed42ce315c7ae370f8 Mon Sep 17 00:00:00 2001 From: Steve Howell Date: Thu, 18 Aug 2016 09:42:17 -0700 Subject: [PATCH] tools: Add a run() method to tools/lint-all. --- tools/lint-all | 804 +++++++++++++++++++++++++------------------------ 1 file changed, 405 insertions(+), 399 deletions(-) diff --git a/tools/lint-all b/tools/lint-all index b7f59dbba9..e2fe266a04 100755 --- a/tools/lint-all +++ b/tools/lint-all @@ -7,6 +7,7 @@ import sys import optparse import subprocess import traceback + try: import lister from typing import cast, Any, Callable, Dict, List, Optional @@ -16,443 +17,448 @@ except ImportError as e: print("If you are using Vagrant, you can `vagrant ssh` to enter the Vagrant guest.") sys.exit(1) -parser = optparse.OptionParser() -parser.add_option('--full', - action='store_true', - help='Check some things we typically ignore') -parser.add_option('--modified', '-m', - action='store_true', - help='Only check modified files') -parser.add_option('--verbose', '-v', - action='store_true', - help='Print verbose timing output') -(options, args) = parser.parse_args() +def run(): + # type: () -> None + parser = optparse.OptionParser() + parser.add_option('--full', + action='store_true', + help='Check some things we typically ignore') + parser.add_option('--modified', '-m', + action='store_true', + help='Only check modified files') + parser.add_option('--verbose', '-v', + action='store_true', + help='Print verbose timing output') + (options, args) = parser.parse_args() -os.chdir(os.path.join(os.path.dirname(__file__), '..')) + os.chdir(os.path.join(os.path.dirname(__file__), '..')) -# Exclude some directories and files from lint checking + # Exclude some directories and files from lint checking -exclude = """ -static/third -frontend_tests/casperjs -zerver/migrations -node_modules -docs/html_unescape.py -zproject/test_settings.py -zproject/dev_settings.py -zproject/settings.py -tools/jslint/jslint.js -api/setup.py -api/integrations/perforce/git_p4.py -puppet/apt/.forge-release -puppet/apt/README.md -static/locale -""".split() + exclude = """ + static/third + frontend_tests/casperjs + zerver/migrations + node_modules + docs/html_unescape.py + zproject/test_settings.py + zproject/dev_settings.py + zproject/settings.py + tools/jslint/jslint.js + api/setup.py + api/integrations/perforce/git_p4.py + puppet/apt/.forge-release + puppet/apt/README.md + static/locale + """.split() -by_lang = cast(Dict[str, List[str]], lister.list_files(args, modified_only=options.modified, - ftypes=['py', 'sh', 'js', 'pp', 'css', 'handlebars', 'html', 'json', 'md', 'txt', 'text'], - use_shebang=True, group_by_ftype=True, exclude=exclude)) + by_lang = cast(Dict[str, List[str]], lister.list_files(args, modified_only=options.modified, + ftypes=['py', 'sh', 'js', 'pp', 'css', 'handlebars', 'html', 'json', 'md', 'txt', 'text'], + use_shebang=True, group_by_ftype=True, exclude=exclude)) -# Invoke the appropriate lint checker for each language, -# and also check files for extra whitespace. + # Invoke the appropriate lint checker for each language, + # and also check files for extra whitespace. -import logging -logging.basicConfig(format="%(asctime)s %(message)s") -logger = logging.getLogger() -if options.verbose: - logger.setLevel(logging.INFO) -else: - logger.setLevel(logging.WARNING) + import logging + logging.basicConfig(format="%(asctime)s %(message)s") + logger = logging.getLogger() + if options.verbose: + logger.setLevel(logging.INFO) + else: + logger.setLevel(logging.WARNING) -def check_pyflakes(): - # type: () -> bool - if not by_lang['py']: - return False - failed = False - pyflakes = subprocess.Popen(['pyflakes'] + by_lang['py'], - stdout = subprocess.PIPE, - stderr = subprocess.PIPE, - universal_newlines = True) + def check_pyflakes(): + # type: () -> bool + if not by_lang['py']: + return False + failed = False + pyflakes = subprocess.Popen(['pyflakes'] + by_lang['py'], + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, + universal_newlines = True) - # pyflakes writes some output (like syntax errors) to stderr. :/ - for pipe in (pyflakes.stdout, pyflakes.stderr): - for ln in pipe: - if options.full or not \ - ('imported but unused' in ln or - 'redefinition of unused' in ln or - ("zerver/models.py" in ln and - " undefined name 'bugdown'" in ln) or - ("scripts/lib/pythonrc.py" in ln and - " import *' used; unable to detect undefined names" in ln) or - ("zerver/lib/tornado_ioloop_logging.py" in ln and - "redefinition of function 'instrument_tornado_ioloop'" in ln) or - ("zephyr_mirror_backend.py:" in ln and - "redefinition of unused 'simplejson' from line" in ln)): - sys.stdout.write(ln) - failed = True - return failed - -RuleList = List[Dict[str, Any]] - -def custom_check_file(fn, rules, skip_rules=None, max_length=None): - # type: (str, RuleList, Optional[Any], Optional[int]) -> bool - failed = False - lineFlag = False - for i, line in enumerate(open(fn)): - line_newline_stripped = line.strip('\n') - line_fully_stripped = line_newline_stripped.strip() - skip = False - lineFlag = True - for rule in skip_rules or []: - if re.match(rule, line): - skip = True - if skip: - continue - for rule in rules: - exclude_list = rule.get('exclude', set()) - if fn in exclude_list: - continue - exclude_list = rule.get('exclude_line', set()) - if (fn, line_fully_stripped) in exclude_list: - continue - try: - line_to_check = line_fully_stripped - if rule.get('strip') is not None: - if rule['strip'] == '\n': - line_to_check = line_newline_stripped - else: - raise Exception("Invalid strip rule") - if re.search(rule['pattern'], line_to_check): - sys.stdout.write(rule['description'] + ' at %s line %s:\n' % (fn, i+1)) - print(line) + # pyflakes writes some output (like syntax errors) to stderr. :/ + for pipe in (pyflakes.stdout, pyflakes.stderr): + for ln in pipe: + if options.full or not \ + ('imported but unused' in ln or + 'redefinition of unused' in ln or + ("zerver/models.py" in ln and + " undefined name 'bugdown'" in ln) or + ("scripts/lib/pythonrc.py" in ln and + " import *' used; unable to detect undefined names" in ln) or + ("zerver/lib/tornado_ioloop_logging.py" in ln and + "redefinition of function 'instrument_tornado_ioloop'" in ln) or + ("zephyr_mirror_backend.py:" in ln and + "redefinition of unused 'simplejson' from line" in ln)): + sys.stdout.write(ln) failed = True - except Exception: - print("Exception with %s at %s line %s" % (rule['pattern'], fn, i+1)) - traceback.print_exc() - if (max_length is not None and len(line) > max_length and - '# type' not in line and 'test' not in fn and 'example' not in fn): - print("Line too long (%s) at %s line %s: %s" % (len(line), fn, i+1, line_newline_stripped)) - lastLine = line - if lineFlag and '\n' not in lastLine: - print("No newline at the end of file. Fix with `sed -i '$a\\' %s`" % (fn,)) - failed = True - return failed + return failed -whitespace_rules = [ - # This linter should be first since bash_rules depends on it. - {'pattern': '\s+$', - 'strip': '\n', - 'description': 'Fix trailing whitespace'}, - {'pattern': '\t', - 'strip': '\n', - 'exclude': set(['zerver/lib/bugdown/codehilite.py', - 'tools/travis/success-http-headers.txt']), - 'description': 'Fix tab-based whitespace'}, + RuleList = List[Dict[str, Any]] + + def custom_check_file(fn, rules, skip_rules=None, max_length=None): + # type: (str, RuleList, Optional[Any], Optional[int]) -> bool + failed = False + lineFlag = False + for i, line in enumerate(open(fn)): + line_newline_stripped = line.strip('\n') + line_fully_stripped = line_newline_stripped.strip() + skip = False + lineFlag = True + for rule in skip_rules or []: + if re.match(rule, line): + skip = True + if skip: + continue + for rule in rules: + exclude_list = rule.get('exclude', set()) + if fn in exclude_list: + continue + exclude_list = rule.get('exclude_line', set()) + if (fn, line_fully_stripped) in exclude_list: + continue + try: + line_to_check = line_fully_stripped + if rule.get('strip') is not None: + if rule['strip'] == '\n': + line_to_check = line_newline_stripped + else: + raise Exception("Invalid strip rule") + if re.search(rule['pattern'], line_to_check): + sys.stdout.write(rule['description'] + ' at %s line %s:\n' % (fn, i+1)) + print(line) + failed = True + except Exception: + print("Exception with %s at %s line %s" % (rule['pattern'], fn, i+1)) + traceback.print_exc() + if (max_length is not None and len(line) > max_length and + '# type' not in line and 'test' not in fn and 'example' not in fn): + print("Line too long (%s) at %s line %s: %s" % (len(line), fn, i+1, line_newline_stripped)) + lastLine = line + if lineFlag and '\n' not in lastLine: + print("No newline at the end of file. Fix with `sed -i '$a\\' %s`" % (fn,)) + failed = True + return failed + + whitespace_rules = [ + # This linter should be first since bash_rules depends on it. + {'pattern': '\s+$', + 'strip': '\n', + 'description': 'Fix trailing whitespace'}, + {'pattern': '\t', + 'strip': '\n', + 'exclude': set(['zerver/lib/bugdown/codehilite.py', + 'tools/travis/success-http-headers.txt']), + 'description': 'Fix tab-based whitespace'}, + ] # type: RuleList + markdown_whitespace_rules = list([rule for rule in whitespace_rules if rule['pattern'] != '\s+$']) + [ + # Two spaces trailing a line with other content is okay--it's a markdown line break. + # This rule finds one space trailing a non-space, three or more trailing spaces, and + # spaces on an empty line. + {'pattern': '((?~"]', + 'description': 'Missing whitespace after "="'}, + {'pattern': '^[ ]*//[A-Za-z0-9]', + 'description': 'Missing space after // in comment'}, + {'pattern': 'if[(]', + 'description': 'Missing space between if and ('}, + {'pattern': 'else{$', + 'description': 'Missing space between else and {'}, + {'pattern': '^else {$', + 'description': 'Write JS else statements on same line as }'}, + {'pattern': '^else if', + 'description': 'Write JS else statements on same line as }'}, + {'pattern': 'button\.text\(["\']', + 'exclude': set(['tools/lint-all', + 'frontend_tests/node_tests/templates.js']), + 'description': 'Argument to button.text should be a literal string enclosed by i18n.t()'}, + {'pattern': 'compose_error\(["\']', + 'exclude': set(['tools/lint-all']), + 'description': 'Argument to compose_error should be a literal string enclosed ' + 'by i18n.t()'}, + {'pattern': 'report_success\(["\']', + 'exclude': set(['tools/lint-all']), + 'description': 'Argument to report_success should be a literal string enclosed ' + 'by i18n.t()'}, + {'pattern': 'report_error\(["\']', + 'exclude': set(['tools/lint-all']), + 'description': 'Argument to report_error should be a literal string enclosed ' + 'by i18n.t()'}, + ]) + whitespace_rules + python_rules = cast(RuleList, [ + {'pattern': '^(?!#)@login_required', + 'description': '@login_required is unsupported; use @zulip_login_required'}, + {'pattern': '".*"%\([a-z_].*\)?$', + 'description': 'Missing space around "%"'}, + {'pattern': "'.*'%\([a-z_].*\)?$", + 'exclude': set(['tools/lint-all']), + 'exclude_line': set([ + ('zerver/views/users.py', + "return json_error(_(\"Email '%(email)s' does not belong to domain '%(domain)s'\") %"), + ]), + 'description': 'Missing space around "%"'}, + # This rule is constructed with + to avoid triggering on itself + {'pattern': " =" + '[^ =>~"]', + 'description': 'Missing whitespace after "="'}, + {'pattern': '":\w[^"]*$', + 'description': 'Missing whitespace after ":"'}, + {'pattern': "':\w[^']*$", + 'description': 'Missing whitespace after ":"'}, + {'pattern': "^\s+[#]\w", + 'strip': '\n', + 'description': 'Missing whitespace after "#"'}, + {'pattern': "== None", + 'exclude': 'tools/lint-all', + 'description': 'Use `is None` to check whether something is None'}, + {'pattern': "type:[(]", + 'description': 'Missing whitespace after ":" in type annotation'}, + {'pattern': "# type [(]", + 'description': 'Missing : after type in type annotation'}, + {'pattern': "#type", + 'exclude': 'tools/lint-all', + 'description': 'Missing whitespace after "#" in type annotation'}, + {'pattern': 'if[(]', + 'description': 'Missing space between if and ('}, + {'pattern': ", [)]", + 'description': 'Unnecessary whitespace between "," and ")"'}, + {'pattern': "% [(]", + 'description': 'Unnecessary whitespace between "%" and "("'}, + # This next check could have false positives, but it seems pretty + # rare; if we find any, they can be added to the exclude list for + # this rule. + {'pattern': '% [a-zA-Z0-9_.]*\)?$', + 'description': 'Used % comprehension without a tuple'}, + # To avoid json_error(_variable) and json_error(_(variable)) + {'pattern': '\Wjson_error\(_\(?\w+\)', + 'exclude': set(['tools/lint-all']), + 'description': 'Argument to json_error should be a literal string enclosed by _()'}, + {'pattern': '\Wjson_error\([^_].+[),]$', + 'exclude': set(['tools/lint-all']), + 'exclude_line': set([ + # function definition + ('zerver/lib/response.py', 'def json_error(msg, data=None, status=400):'), + # No need to worry about the following as the translation strings + # are already captured + ('zerver/middleware.py', + 'return json_error(exception.to_json_error_msg(), status=status_code)'), + ('zerver/tornadoviews.py', 'return json_error(result["message"])'), + ('zerver/views/__init__.py', + 'return json_error(data=error_data, msg=ret_error)'), + ('zerver/views/streams.py', 'return json_error(property_conversion)'), + # We can't do anything about this. + ('zerver/views/realm_emoji.py', 'return json_error(e.messages[0])'), + ]), + 'description': 'Argument to json_error should a literal string enclosed by _()'}, + # To avoid JsonableError(_variable) and JsonableError(_(variable)) + {'pattern': '\WJsonableError\(_\(?\w.+\)', + 'exclude': set(['tools/lint-all']), + 'description': 'Argument to JsonableError should be a literal string enclosed by _()'}, + {'pattern': '\WJsonableError\([^_].+\)', + 'exclude': set(['tools/lint-all']), + 'exclude_line': set([ + # class definition + ('zerver/lib/request.py', 'class JsonableError(Exception):'), + # No need to worry about the following as the translation strings + # are already captured + ('zerver/decorator.py', 'raise JsonableError(reason % (role,))'), + ('zerver/lib/actions.py', 'raise JsonableError(e.messages[0])'), + ('zerver/views/messages.py', 'raise JsonableError(error)'), + ('zerver/lib/request.py', 'raise JsonableError(error)'), + ]), + 'description': 'Argument to JsonableError should be a literal string enclosed by _()'}, + {'pattern': '([a-zA-Z0-9_]+)=REQ\([\'"]\\1[\'"]', + 'description': 'REQ\'s first argument already defaults to parameter name'}, + {'pattern': 'self\.client\.(get|post|patch|put|delete)', + 'exclude': set(['zilencer/tests.py']), + 'description': \ + '''Do not call self.client directly for put/patch/post/get. + See WRAPPER_COMMENT in test_helpers.py for details. + '''}, + + ]) + whitespace_rules + bash_rules = [ + {'pattern': '#!.*sh [-xe]', + 'description': 'Fix shebang line with proper call to /usr/bin/env for Bash path, change -x|-e switches to set -x|set -e'}, + ] + whitespace_rules[0:1] # type: RuleList + css_rules = [ + {'pattern': '^[^:]*:\S[^:]*;$', + 'description': "Missing whitespace after : in CSS"}, + {'pattern': '[a-z]{', + 'description': "Missing whitespace before '{' in CSS."}, + {'pattern': '{\w', + 'description': "Missing whitespace after '{' in CSS (should be newline)."}, + ] + whitespace_rules # type: RuleList + handlebars_rules = whitespace_rules + html_rules = whitespace_rules + [ + {'pattern': 'placeholder="[^{]', + 'description': "`placeholder` value should be translatable."}, + {'pattern': "placeholder='[^{]", + 'description': "`placeholder` value should be translatable."}, + ] # type: RuleList + json_rules = [] # type: RuleList # just fix newlines at ends of files + markdown_rules = markdown_whitespace_rules + [ + {'pattern': ' [jJ]avascript', + 'exclude': set(['README.dev.md']), # temporary exclusion to avoid merge conflicts + 'description': "javascript should be spelled JavaScript"}, ] # type: RuleList -markdown_whitespace_rules = list([rule for rule in whitespace_rules if rule['pattern'] != '\s+$']) + [ - # Two spaces trailing a line with other content is okay--it's a markdown line break. - # This rule finds one space trailing a non-space, three or more trailing spaces, and - # spaces on an empty line. - {'pattern': '((?~"]', - 'description': 'Missing whitespace after "="'}, - {'pattern': '^[ ]*//[A-Za-z0-9]', - 'description': 'Missing space after // in comment'}, - {'pattern': 'if[(]', - 'description': 'Missing space between if and ('}, - {'pattern': 'else{$', - 'description': 'Missing space between else and {'}, - {'pattern': '^else {$', - 'description': 'Write JS else statements on same line as }'}, - {'pattern': '^else if', - 'description': 'Write JS else statements on same line as }'}, - {'pattern': 'button\.text\(["\']', - 'exclude': set(['tools/lint-all', - 'frontend_tests/node_tests/templates.js']), - 'description': 'Argument to button.text should be a literal string enclosed by i18n.t()'}, - {'pattern': 'compose_error\(["\']', - 'exclude': set(['tools/lint-all']), - 'description': 'Argument to compose_error should be a literal string enclosed ' - 'by i18n.t()'}, - {'pattern': 'report_success\(["\']', - 'exclude': set(['tools/lint-all']), - 'description': 'Argument to report_success should be a literal string enclosed ' - 'by i18n.t()'}, - {'pattern': 'report_error\(["\']', - 'exclude': set(['tools/lint-all']), - 'description': 'Argument to report_error should be a literal string enclosed ' - 'by i18n.t()'}, - ]) + whitespace_rules -python_rules = cast(RuleList, [ - {'pattern': '^(?!#)@login_required', - 'description': '@login_required is unsupported; use @zulip_login_required'}, - {'pattern': '".*"%\([a-z_].*\)?$', - 'description': 'Missing space around "%"'}, - {'pattern': "'.*'%\([a-z_].*\)?$", - 'exclude': set(['tools/lint-all']), - 'exclude_line': set([ - ('zerver/views/users.py', - "return json_error(_(\"Email '%(email)s' does not belong to domain '%(domain)s'\") %"), - ]), - 'description': 'Missing space around "%"'}, - # This rule is constructed with + to avoid triggering on itself - {'pattern': " =" + '[^ =>~"]', - 'description': 'Missing whitespace after "="'}, - {'pattern': '":\w[^"]*$', - 'description': 'Missing whitespace after ":"'}, - {'pattern': "':\w[^']*$", - 'description': 'Missing whitespace after ":"'}, - {'pattern': "^\s+[#]\w", - 'strip': '\n', - 'description': 'Missing whitespace after "#"'}, - {'pattern': "== None", - 'exclude': 'tools/lint-all', - 'description': 'Use `is None` to check whether something is None'}, - {'pattern': "type:[(]", - 'description': 'Missing whitespace after ":" in type annotation'}, - {'pattern': "# type [(]", - 'description': 'Missing : after type in type annotation'}, - {'pattern': "#type", - 'exclude': 'tools/lint-all', - 'description': 'Missing whitespace after "#" in type annotation'}, - {'pattern': 'if[(]', - 'description': 'Missing space between if and ('}, - {'pattern': ", [)]", - 'description': 'Unnecessary whitespace between "," and ")"'}, - {'pattern': "% [(]", - 'description': 'Unnecessary whitespace between "%" and "("'}, - # This next check could have false positives, but it seems pretty - # rare; if we find any, they can be added to the exclude list for - # this rule. - {'pattern': '% [a-zA-Z0-9_.]*\)?$', - 'description': 'Used % comprehension without a tuple'}, - # To avoid json_error(_variable) and json_error(_(variable)) - {'pattern': '\Wjson_error\(_\(?\w+\)', - 'exclude': set(['tools/lint-all']), - 'description': 'Argument to json_error should be a literal string enclosed by _()'}, - {'pattern': '\Wjson_error\([^_].+[),]$', - 'exclude': set(['tools/lint-all']), - 'exclude_line': set([ - # function definition - ('zerver/lib/response.py', 'def json_error(msg, data=None, status=400):'), - # No need to worry about the following as the translation strings - # are already captured - ('zerver/middleware.py', - 'return json_error(exception.to_json_error_msg(), status=status_code)'), - ('zerver/tornadoviews.py', 'return json_error(result["message"])'), - ('zerver/views/__init__.py', - 'return json_error(data=error_data, msg=ret_error)'), - ('zerver/views/streams.py', 'return json_error(property_conversion)'), - # We can't do anything about this. - ('zerver/views/realm_emoji.py', 'return json_error(e.messages[0])'), - ]), - 'description': 'Argument to json_error should a literal string enclosed by _()'}, - # To avoid JsonableError(_variable) and JsonableError(_(variable)) - {'pattern': '\WJsonableError\(_\(?\w.+\)', - 'exclude': set(['tools/lint-all']), - 'description': 'Argument to JsonableError should be a literal string enclosed by _()'}, - {'pattern': '\WJsonableError\([^_].+\)', - 'exclude': set(['tools/lint-all']), - 'exclude_line': set([ - # class definition - ('zerver/lib/request.py', 'class JsonableError(Exception):'), - # No need to worry about the following as the translation strings - # are already captured - ('zerver/decorator.py', 'raise JsonableError(reason % (role,))'), - ('zerver/lib/actions.py', 'raise JsonableError(e.messages[0])'), - ('zerver/views/messages.py', 'raise JsonableError(error)'), - ('zerver/lib/request.py', 'raise JsonableError(error)'), - ]), - 'description': 'Argument to JsonableError should be a literal string enclosed by _()'}, - {'pattern': '([a-zA-Z0-9_]+)=REQ\([\'"]\\1[\'"]', - 'description': 'REQ\'s first argument already defaults to parameter name'}, - {'pattern': 'self\.client\.(get|post|patch|put|delete)', - 'exclude': set(['zilencer/tests.py']), - 'description': \ -'''Do not call self.client directly for put/patch/post/get. -See WRAPPER_COMMENT in test_helpers.py for details. -'''}, + txt_rules = whitespace_rules - ]) + whitespace_rules -bash_rules = [ - {'pattern': '#!.*sh [-xe]', - 'description': 'Fix shebang line with proper call to /usr/bin/env for Bash path, change -x|-e switches to set -x|set -e'}, - ] + whitespace_rules[0:1] # type: RuleList -css_rules = [ - {'pattern': '^[^:]*:\S[^:]*;$', - 'description': "Missing whitespace after : in CSS"}, - {'pattern': '[a-z]{', - 'description': "Missing whitespace before '{' in CSS."}, - {'pattern': '{\w', - 'description': "Missing whitespace after '{' in CSS (should be newline)."}, - ] + whitespace_rules # type: RuleList -handlebars_rules = whitespace_rules -html_rules = whitespace_rules + [ - {'pattern': 'placeholder="[^{]', - 'description': "`placeholder` value should be translatable."}, - {'pattern': "placeholder='[^{]", - 'description': "`placeholder` value should be translatable."}, - ] # type: RuleList -json_rules = [] # type: RuleList # just fix newlines at ends of files -markdown_rules = markdown_whitespace_rules + [ - {'pattern': ' [jJ]avascript', - 'exclude': set(['README.dev.md']), # temporary exclusion to avoid merge conflicts - 'description': "javascript should be spelled JavaScript"}, -] # type: RuleList -txt_rules = whitespace_rules + def check_custom_checks_py(): + # type: () -> bool + failed = False -def check_custom_checks_py(): - # type: () -> bool - failed = False + for fn in by_lang['py']: + if custom_check_file(fn, python_rules, max_length=180): + failed = True + return failed - for fn in by_lang['py']: - if custom_check_file(fn, python_rules, max_length=180): - failed = True - return failed + def check_custom_checks_nonpy(): + # type: () -> bool + failed = False -def check_custom_checks_nonpy(): - # type: () -> bool - failed = False + for fn in by_lang['js']: + if custom_check_file(fn, js_rules): + failed = True - for fn in by_lang['js']: - if custom_check_file(fn, js_rules): - failed = True + for fn in by_lang['sh']: + if custom_check_file(fn, bash_rules): + failed = True - for fn in by_lang['sh']: - if custom_check_file(fn, bash_rules): - failed = True + for fn in by_lang['css']: + if custom_check_file(fn, css_rules): + failed = True - for fn in by_lang['css']: - if custom_check_file(fn, css_rules): - failed = True + for fn in by_lang['handlebars']: + if custom_check_file(fn, handlebars_rules): + failed = True - for fn in by_lang['handlebars']: - if custom_check_file(fn, handlebars_rules): - failed = True + for fn in by_lang['html']: + if custom_check_file(fn, html_rules): + failed = True - for fn in by_lang['html']: - if custom_check_file(fn, html_rules): - failed = True + for fn in by_lang['json']: + if custom_check_file(fn, json_rules): + failed = True - for fn in by_lang['json']: - if custom_check_file(fn, json_rules): - failed = True + for fn in by_lang['md']: + if custom_check_file(fn, markdown_rules): + failed = True - for fn in by_lang['md']: - if custom_check_file(fn, markdown_rules): - failed = True + for fn in by_lang['txt'] + by_lang['text']: + if custom_check_file(fn, txt_rules): + failed = True - for fn in by_lang['txt'] + by_lang['text']: - if custom_check_file(fn, txt_rules): - failed = True + return failed - return failed + lint_functions = {} # type: Dict[str, Callable[[], int]] -lint_functions = {} # type: Dict[str, Callable[[], int]] + def run_parallel(): + # type: () -> bool + pids = [] + for name, func in lint_functions.items(): + pid = os.fork() + if pid == 0: + logging.info("start " + name) + result = func() + logging.info("finish " + name) + sys.stdout.flush() + sys.stderr.flush() + os._exit(result) + pids.append(pid) + failed = False -def run_parallel(): - # type: () -> bool - pids = [] - for name, func in lint_functions.items(): - pid = os.fork() - if pid == 0: - logging.info("start " + name) - result = func() - logging.info("finish " + name) - sys.stdout.flush() - sys.stderr.flush() - os._exit(result) - pids.append(pid) - failed = False + for pid in pids: + (_, status) = os.waitpid(pid, 0) + if status != 0: + failed = True + return failed - for pid in pids: - (_, status) = os.waitpid(pid, 0) - if status != 0: - failed = True - return failed + def lint(func): + # type: (Callable[[], int]) -> Callable[[], int] + lint_functions[func.__name__] = func + return func -def lint(func): - # type: (Callable[[], int]) -> Callable[[], int] - lint_functions[func.__name__] = func - return func + try: + # Make the lint output bright red + sys.stdout.write('\x1B[1;31m') + sys.stdout.flush() -try: - # Make the lint output bright red - sys.stdout.write('\x1B[1;31m') - sys.stdout.flush() + @lint + def templates(): + # type: () -> int + args = ['tools/check-templates'] + if options.modified: + args.append('-m') + result = subprocess.call(args) + return result - @lint - def templates(): - # type: () -> int - args = ['tools/check-templates'] - if options.modified: - args.append('-m') - result = subprocess.call(args) - return result + @lint + def add_class(): + # type: () -> int + result = subprocess.call(['tools/find-add-class']) + return result - @lint - def add_class(): - # type: () -> int - result = subprocess.call(['tools/find-add-class']) - return result + @lint + def css(): + # type: () -> int + result = subprocess.call(['tools/check-css']) + return result - @lint - def css(): - # type: () -> int - result = subprocess.call(['tools/check-css']) - return result + @lint + def jslint(): + # type: () -> int + result = subprocess.call(['tools/node', 'tools/jslint/check-all.js'] + + by_lang['js']) + return result - @lint - def jslint(): - # type: () -> int - result = subprocess.call(['tools/node', 'tools/jslint/check-all.js'] - + by_lang['js']) - return result + @lint + def puppet(): + # type: () -> int + if not by_lang['pp']: + return 0 + result = subprocess.call(['puppet', 'parser', 'validate'] + by_lang['pp']) + return result - @lint - def puppet(): - # type: () -> int - if not by_lang['pp']: - return 0 - result = subprocess.call(['puppet', 'parser', 'validate'] + by_lang['pp']) - return result + @lint + def custom_py(): + # type: () -> int + failed = check_custom_checks_py() + return 1 if failed else 0 - @lint - def custom_py(): - # type: () -> int - failed = check_custom_checks_py() - return 1 if failed else 0 + @lint + def custom_nonpy(): + # type: () -> int + failed = check_custom_checks_nonpy() + return 1 if failed else 0 - @lint - def custom_nonpy(): - # type: () -> int - failed = check_custom_checks_nonpy() - return 1 if failed else 0 + @lint + def pyflakes(): + # type: () -> int + failed = check_pyflakes() + return 1 if failed else 0 - @lint - def pyflakes(): - # type: () -> int - failed = check_pyflakes() - return 1 if failed else 0 + failed = run_parallel() - failed = run_parallel() + sys.exit(1 if failed else 0) - sys.exit(1 if failed else 0) + finally: + # Restore normal terminal colors + sys.stdout.write('\x1B[0m') -finally: - # Restore normal terminal colors - sys.stdout.write('\x1B[0m') +if __name__ == '__main__': + run()