def test_lint_one_empty_lint(self): linter1 = functools.partial( linters.lint_command, 'l1', 'linter1', ['-f'], '^Line (?P<line>{lines}): (?P<message>.*)$') linter2 = functools.partial( linters.lint_command, 'l2', 'linter2', [], '^ line (?P<line>{lines}): (?P<message>.*)$') config = {'extensions': {'.txt': [linter1, linter2]}} outputs = [b'', os.linesep.join([' line 4: 4']).encode('utf-8')] with mock.patch('subprocess.check_output', side_effect=outputs) as check_output, \ mock.patch('os.path.getmtime', side_effect=[1, 0, 1, 0]): filename = 'foo.txt' self.assertEqual( { filename: { 'comments': [ { 'line': 4, 'message': '4' }, ], }, }, linters.lint(filename, lines=[4, 5], config=config)) expected_calls = [ mock.call(['linter1', '-f', 'foo.txt'], stderr=subprocess.STDOUT), mock.call(['linter2', 'foo.txt'], stderr=subprocess.STDOUT) ] self.assertEqual(expected_calls, check_output.call_args_list)
def test_lint_one_empty_lint(self): linter1 = functools.partial( linters.lint_command, 'l1', 'linter1', ['-f'], '^Line (?P<line>{lines}): (?P<message>.*)$') linter2 = functools.partial( linters.lint_command, 'l2', 'linter2', [], '^ line (?P<line>{lines}): (?P<message>.*)$') config = {'.txt': [linter1, linter2]} outputs = [b'', os.linesep.join([' line 4: 4']).encode('utf-8')] with mock.patch('subprocess.check_output', side_effect=outputs) as check_output, \ mock.patch('os.path.getmtime', side_effect=[1, 0, 1, 0]): filename = 'foo.txt' self.assertEqual({ filename: { 'comments': [ { 'line': 4, 'message': '4' }, ], }, }, linters.lint(filename, lines=[4, 5], config=config)) expected_calls = [ mock.call( ['linter1', '-f', 'foo.txt'], stderr=subprocess.STDOUT), mock.call(['linter2', 'foo.txt'], stderr=subprocess.STDOUT) ] self.assertEqual(expected_calls, check_output.call_args_list)
def test_lint_all_empty_lint(self): linter1 = functools.partial( linters.lint_command, 'l1', 'linter1', ['-f'], '^Line {lines}:') linter2 = functools.partial( linters.lint_command, 'l2', 'linter2', [], '^ line {lines}:') config = { '.txt': [linter1, linter2] } outputs = [b'', b''] with mock.patch('subprocess.check_output', side_effect=outputs) as check_output, \ mock.patch('os.path.getmtime', side_effect=[1, 0, 1, 0]): filename = 'foo.txt' self.assertEqual( { filename: { 'comments': [] } }, linters.lint(filename, lines=[4, 5], config=config)) expected_calls = [ mock.call(['linter1', '-f', 'foo.txt'], stderr=subprocess.STDOUT), mock.call(['linter2', 'foo.txt'], stderr=subprocess.STDOUT)] self.assertEqual(expected_calls, check_output.call_args_list)
def test_lint_two_missing_programs(self): linter1 = functools.partial(linters.missing_requirements_command, ['p1', 'p2'], 'Install p1 and p2') config = {'.txt': [linter1, linter1]} output = linters.lint('foo.txt', lines=[4, 5], config=config) self.assertEqual(2, len(output['foo.txt']['skipped'])) output['foo.txt']['skipped'] = [] self.assertEqual({'foo.txt': {'skipped': []}}, output)
def test_lint_two_missing_programs(self): linter1 = functools.partial(linters.missing_requirements_command, 'l1', ['p1', 'p2']) config = {'extensions': {'.txt': [linter1, linter1]}} output = linters.lint('foo.txt', lines=[4, 5], config=config) self.assertEqual(2, len(output['foo.txt']['skipped'])) output['foo.txt']['skipped'] = [] self.assertEqual({'foo.txt': {'skipped': []}}, output)
def test_lint_output_is_sorted(self): linter1 = functools.partial( linters.lint_command, 'l1', 'linter1', ['-f'], '^Line (?P<line>{lines}): (?P<message>.*)$') linter2 = functools.partial( linters.lint_command, 'l2', 'linter2', [], '^ line (?P<line>{lines}): (?P<message>.*)$') linter3 = functools.partial(linters.lint_command, 'l3', 'linter3', [], '^(?P<message>.*)$') linter4 = functools.partial( linters.lint_command, 'l4', 'linter4', [], r'^(?P<line>{lines}):(?P<column>\d+): (?P<message>.*)$') config = {'extensions': {'.txt': [linter1, linter2, linter3, linter4]}} outputs = [ os.linesep.join(['Line 5: 5', 'Line 1: 1']).encode('utf-8'), os.linesep.join([' line 4: 4']).encode('utf-8'), os.linesep.join(['message']).encode('utf-8'), os.linesep.join(['4:10: 4.a', '4:1: 4.b']).encode('utf-8'), ] with mock.patch('subprocess.check_output', side_effect=outputs), \ mock.patch('os.path.getmtime', side_effect=[1, 0] * 4): filename = 'foo.txt' self.assertEqual( { filename: { 'comments': [ { 'message': 'message', }, { 'line': 1, 'message': '1', }, { 'line': 4, 'message': '4', }, { 'line': 4, 'column': 1, 'message': '4.b', }, { 'line': 4, 'column': 10, 'message': '4.a', }, { 'line': 5, 'message': '5', }, ], }, }, linters.lint(filename, lines=None, config=config))
def test_lint_output_is_sorted(self): linter1 = functools.partial( linters.lint_command, 'l1', 'linter1', ['-f'], '^Line (?P<line>{lines}): (?P<message>.*)$') linter2 = functools.partial( linters.lint_command, 'l2', 'linter2', [], '^ line (?P<line>{lines}): (?P<message>.*)$') linter3 = functools.partial(linters.lint_command, 'l3', 'linter3', [], '^(?P<message>.*)$') linter4 = functools.partial( linters.lint_command, 'l4', 'linter4', [], r'^(?P<line>{lines}):(?P<column>\d+): (?P<message>.*)$') config = {'.txt': [linter1, linter2, linter3, linter4]} outputs = [ os.linesep.join(['Line 5: 5', 'Line 1: 1']).encode('utf-8'), os.linesep.join([' line 4: 4']).encode('utf-8'), os.linesep.join(['message']).encode('utf-8'), os.linesep.join(['4:10: 4.a', '4:1: 4.b']).encode('utf-8'), ] with mock.patch('subprocess.check_output', side_effect=outputs), \ mock.patch('os.path.getmtime', side_effect=[1, 0] * 4): filename = 'foo.txt' self.assertEqual({ filename: { 'comments': [ { 'message': 'message', }, { 'line': 1, 'message': '1', }, { 'line': 4, 'message': '4', }, { 'line': 4, 'column': 1, 'message': '4.b', }, { 'line': 4, 'column': 10, 'message': '4.a', }, { 'line': 5, 'message': '5', }, ], }, }, linters.lint(filename, lines=None, config=config))
def test_lint_extension_not_defined(self): config = {} output = linters.lint('foo.txt', lines=[4, 5], config=config) self.assertEqual(1, len(output['foo.txt']['skipped'])) output['foo.txt']['skipped'] = [] self.assertEqual( { 'foo.txt': { 'skipped': [] } }, output)
def process_file(vcs, commit, force, gitlint_config, file_data): """Lint the file Returns: The results from the linter. """ filename, extra_data = file_data if force: modified_lines = None else: modified_lines = vcs.modified_lines( filename, extra_data, commit=commit) result = linters.lint(filename, modified_lines, gitlint_config) result = result[filename] return filename, result
def process_file(vcs, commit, force, linter_config, fixer_config, fix, fix_all, file_data): """Lint and optionally fix the file. Returns: The results from the linter. """ filename, extra_data = file_data if fix: fixers.fix(filename, fixer_config, get_vcs_modified_lines(vcs, force, filename, extra_data, commit)) elif fix_all: fixers.fix(filename, fixer_config) result = linters.lint(filename, get_vcs_modified_lines(vcs, force, filename, extra_data, commit), linter_config) result = result[filename] return filename, result
def test_lint_all_empty_lint(self): linter1 = functools.partial(linters.lint_command, 'l1', 'linter1', ['-f'], '^Line {lines}:') linter2 = functools.partial(linters.lint_command, 'l2', 'linter2', [], '^ line {lines}:') config = {'extensions': {'.txt': [linter1, linter2]}} outputs = [b'', b''] with mock.patch('subprocess.check_output', side_effect=outputs) as check_output, \ mock.patch('os.path.getmtime', side_effect=[1, 0, 1, 0]): filename = 'foo.txt' self.assertEqual({filename: { 'comments': [] }}, linters.lint(filename, lines=[4, 5], config=config)) expected_calls = [ mock.call(['linter1', '-f', 'foo.txt'], stderr=subprocess.STDOUT), mock.call(['linter2', 'foo.txt'], stderr=subprocess.STDOUT) ] self.assertEqual(expected_calls, check_output.call_args_list)
def main(argv, stdout=sys.stdout, stderr=sys.stderr): """Main gitlint routine. To be called from scripts.""" # Wrap sys stdout for python 2, so print can understand unicode. linesep = os.linesep if sys.version_info[0] < 3: if stdout == sys.stdout: stdout = codecs.getwriter("utf-8")(stdout) if stderr == sys.stderr: stderr = codecs.getwriter("utf-8")(stderr) linesep = unicode(os.linesep) arguments = docopt.docopt(__doc__, argv=argv[1:], version='git-lint v%s' % __VERSION__) json_output = arguments['--json'] vcs, repository_root = get_vcs_root() if vcs is None: stderr.write('fatal: Not a git repository' + os.linesep) return 128 commit = None if arguments['--last-commit']: commit = vcs.last_commit() if arguments['FILENAME']: invalid_filenames = find_invalid_filenames(arguments['FILENAME'], repository_root) if invalid_filenames: invalid_filenames.append(('', '')) stderr.write( os.linesep.join(invalid[1] for invalid in invalid_filenames)) return 2 changed_files = vcs.modified_files(repository_root, tracked_only=arguments['--tracked'], commit=commit) modified_files = {} for filename in arguments['FILENAME']: normalized_filename = os.path.abspath(filename) modified_files[normalized_filename] = changed_files.get( normalized_filename) else: modified_files = vcs.modified_files(repository_root, tracked_only=arguments['--tracked'], commit=commit) linter_not_found = False files_with_problems = 0 gitlint_config = get_config(repository_root) json_result = {} error = termcolor.colored('ERROR', 'red', attrs=('bold',)) skipped = termcolor.colored('SKIPPED', 'yellow', attrs=('bold',)) for filename in sorted(modified_files.keys()): rel_filename = os.path.relpath(filename) if not json_output: stdout.write('Linting file: %s%s' % (termcolor.colored(rel_filename, attrs=('bold',)), linesep)) if arguments['--force']: modified_lines = None else: modified_lines = vcs.modified_lines(filename, modified_files[filename], commit=commit) result = linters.lint( filename, modified_lines, gitlint_config) result = result[filename] output = '' if result.get('error'): output += os.linesep.join( '%s: %s' % (error, reason) for reason in result.get('error') ) linter_not_found = True if result.get('skipped'): output += os.linesep.join( '%s: %s' % (skipped, reason) for reason in result.get('skipped') ) if result.get('comments', []) == []: if not output: output += termcolor.colored('OK', 'green', attrs=('bold',)) else: files_with_problems += 1 messages = [] for data in result['comments']: formatted_message = format_comment(data) messages.append(formatted_message) data['formatted_message'] = formatted_message output += os.linesep.join(messages) if json_output: json_result[filename] = result else: stdout.write(output) stdout.write(linesep + linesep) if json_output: # Hack to convert to unicode, Python3 returns unicode, wheres Python2 # returns str. stdout.write( json.dumps(json_result, ensure_ascii=False).encode('utf-8').decode('utf-8')) if files_with_problems > 0: return 1 if linter_not_found: return 4 return 0
def main(argv, stdout=sys.stdout, stderr=sys.stderr): """Main gitlint routine. To be called from scripts.""" # Wrap sys stdout for python 2, so print can understand unicode. linesep = os.linesep if sys.version_info[0] < 3: if stdout == sys.stdout: stdout = codecs.getwriter("utf-8")(stdout) if stderr == sys.stderr: stderr = codecs.getwriter("utf-8")(stderr) linesep = unicode(os.linesep) arguments = docopt.docopt(__doc__, argv=argv[1:], version='git-lint v%s' % __VERSION__) json_output = arguments['--json'] vcs, repository_root = get_vcs_root() if vcs is None: stderr.write('fatal: Not a git repository' + linesep) return 128 commit = None if arguments['--last-commit']: commit = vcs.last_commit() if arguments['FILENAME']: invalid_filenames = find_invalid_filenames(arguments['FILENAME'], repository_root) if invalid_filenames: invalid_filenames.append(('', '')) stderr.write( linesep.join(invalid[1] for invalid in invalid_filenames)) return 2 changed_files = vcs.modified_files(repository_root, tracked_only=arguments['--tracked'], commit=commit) modified_files = {} for filename in arguments['FILENAME']: normalized_filename = os.path.abspath(filename) modified_files[normalized_filename] = changed_files.get( normalized_filename) else: modified_files = vcs.modified_files( repository_root, tracked_only=arguments['--tracked'], commit=commit) linter_not_found = False files_with_problems = 0 gitlint_config = get_config(repository_root) json_result = {} for filename in sorted(modified_files.keys()): rel_filename = os.path.relpath(filename) if not json_output: stdout.write( 'Linting file: %s%s' % (termcolor.colored(rel_filename, attrs=('bold', )), linesep)) if arguments['--force']: modified_lines = None else: modified_lines = vcs.modified_lines(filename, modified_files[filename], commit=commit) result = linters.lint(filename, modified_lines, gitlint_config) result = result[filename] output_lines = [] if result.get('error'): output_lines.extend('%s: %s' % (ERROR, reason) for reason in result.get('error')) linter_not_found = True if result.get('skipped'): output_lines.extend('%s: %s' % (SKIPPED, reason) for reason in result.get('skipped')) if result.get('comments', []) == []: if not output_lines: output_lines.append(OK) else: files_with_problems += 1 for data in result['comments']: formatted_message = format_comment(data) output_lines.append(formatted_message) data['formatted_message'] = formatted_message if json_output: json_result[filename] = result else: output = linesep.join(output_lines) stdout.write(output) stdout.write(linesep + linesep) if json_output: # Hack to convert to unicode, Python3 returns unicode, wheres Python2 # returns str. stdout.write( json.dumps(json_result, ensure_ascii=False).encode('utf-8').decode('utf-8')) if files_with_problems > 0: return 1 if linter_not_found: return 4 return 0
def test_lint_extension_not_defined(self, _): config = {'extensions': {}} output = linters.lint('foo.txt', lines=[4, 5], config=config) self.assertEqual(1, len(output['foo.txt']['skipped'])) output['foo.txt']['skipped'] = [] self.assertEqual({'foo.txt': {'skipped': []}}, output)