def command_sanity_code_smell(args, _, script): """ :type args: SanityConfig :type _: SanityTargets :type script: str :rtype: SanityResult """ test = os.path.splitext(os.path.basename(script))[0] cmd = [script] env = ansible_environment(args, color=False) try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status: summary = str(SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout)) return SanityFailure(test, summary=summary) return SanitySuccess(test)
def test(self, args): """ :type args: SanityConfig :rtype: SanityResult """ cmd = [self.path] env = ansible_environment(args, color=False) try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status: summary = str( SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout)) return SanityFailure(self.name, summary=summary) return SanitySuccess(self.name)
def test(self, args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: TestResult """ if args.python_version in UNSUPPORTED_PYTHON_VERSIONS: display.warning('Skipping rstcheck on unsupported Python version %s.' % args.python_version) return SanitySkipped(self.name) ignore_file = os.path.join(ANSIBLE_ROOT, 'test/sanity/rstcheck/ignore-substitutions.txt') ignore_substitutions = sorted(set(read_lines_without_comments(ignore_file, remove_blank_lines=True))) settings = self.load_settings(args, None) paths = sorted(i.path for i in targets.include if os.path.splitext(i.path)[1] in ('.rst',)) paths = settings.filter_skipped_paths(paths) if not paths: return SanitySkipped(self.name) cmd = [ args.python_executable, '-m', 'rstcheck', '--report', 'warning', '--ignore-substitutions', ','.join(ignore_substitutions), ] + paths try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stdout: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySuccess(self.name) pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+): \((?P<level>INFO|WARNING|ERROR|SEVERE)/[0-4]\) (?P<message>.*)$' results = parse_to_list_of_dict(pattern, stderr) results = [SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=0, level=r['level'], ) for r in results] settings.process_errors(results, paths) if results: return SanityFailure(self.name, messages=results) return SanitySuccess(self.name)
def command_sanity_code_smell(args, _, script): """ :type args: SanityConfig :type _: SanityTargets :type script: str :rtype: SanityResult """ test = os.path.splitext(os.path.basename(script))[0] cmd = [script] env = ansible_environment(args) # Since the output from scripts end up in other places besides the console, we don't want color here. env.pop('ANSIBLE_FORCE_COLOR') try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status: summary = str( SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout)) return SanityFailure(test, summary=summary) return SanitySuccess(test)
def command_sanity_ansible_doc(args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str """ with open('test/sanity/ansible-doc/skip.txt', 'r') as skip_fd: skip_modules = set(skip_fd.read().splitlines()) modules = sorted( set(m for i in targets.include_external for m in i.modules) - set(m for i in targets.exclude_external for m in i.modules) - skip_modules) if not modules: display.info('No tests applicable.', verbosity=1) return env = ansible_environment(args) cmd = ['ansible-doc'] + modules stdout, stderr = intercept_command(args, cmd, env=env, capture=True, python_version=python_version) if stderr: # consider any output on stderr an error, even though the return code is zero raise SubprocessError(cmd, stderr=stderr) if stdout: display.info(stdout.strip(), verbosity=3)
def command_sanity_shellcheck(args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: SanityResult """ test = 'shellcheck' with open('test/sanity/shellcheck/skip.txt', 'r') as skip_fd: skip_paths = set(skip_fd.read().splitlines()) with open('test/sanity/shellcheck/exclude.txt', 'r') as exclude_fd: exclude = set(exclude_fd.read().splitlines()) paths = sorted(i.path for i in targets.include if os.path.splitext(i.path)[1] == '.sh' and i.path not in skip_paths) if not paths: return SanitySkipped(test) cmd = [ 'shellcheck', '-e', ','.join(sorted(exclude)), '--format', 'checkstyle', ] + paths try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status > 1: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySkipped(test) # json output is missing file paths in older versions of shellcheck, so we'll use xml instead root = fromstring(stdout) # type: Element results = [] for item in root: # type: Element for entry in item: # type: Element results.append(SanityMessage( message=entry.attrib['message'], path=item.attrib['name'], line=int(entry.attrib['line']), column=int(entry.attrib['column']), level=entry.attrib['severity'], code=entry.attrib['source'].replace('ShellCheck.', ''), )) if results: return SanityFailure(test, messages=results) return SanitySuccess(test)
def pylint(args, context, paths, plugin_dir, plugin_names): # type: (SanityConfig, str, t.List[str], str, t.List[str]) -> t.List[t.Dict[str, str]] """Run pylint using the config specified by the context on the specified paths.""" rcfile = os.path.join(ANSIBLE_ROOT, 'test/sanity/pylint/config/%s' % context.split('/')[0]) if not os.path.exists(rcfile): rcfile = os.path.join(ANSIBLE_ROOT, 'test/sanity/pylint/config/default') parser = ConfigParser() parser.read(rcfile) if parser.has_section('ansible-test'): config = dict(parser.items('ansible-test')) else: config = dict() disable_plugins = set(i.strip() for i in config.get('disable-plugins', '').split(',') if i) load_plugins = set(plugin_names) - disable_plugins cmd = [ args.python_executable, '-m', 'pylint', '--jobs', '0', '--reports', 'n', '--max-line-length', '160', '--rcfile', rcfile, '--output-format', 'json', '--load-plugins', ','.join(load_plugins), ] + paths append_python_path = [plugin_dir] if data_context().content.collection: append_python_path.append(data_context().content.collection.root) env = ansible_environment(args) env['PYTHONPATH'] += os.path.pathsep + os.path.pathsep.join(append_python_path) if paths: display.info('Checking %d file(s) in context "%s" with config: %s' % (len(paths), context, rcfile), verbosity=1) try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status >= 32: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) else: stdout = None if not args.explain and stdout: messages = json.loads(stdout) else: messages = [] return messages
def test(self, args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str :rtype: TestResult """ settings = self.load_processor(args, python_version) paths = [target.path for target in targets.include] cmd = [ find_python(python_version), os.path.join(SANITY_ROOT, 'compile', 'compile.py') ] data = '\n'.join(paths) display.info(data, verbosity=4) try: stdout, stderr = run_command(args, cmd, data=data, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySuccess(self.name, python_version=python_version) pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<message>.*)$' results = parse_to_list_of_dict(pattern, stdout) results = [ SanityMessage( message=r['message'], path=r['path'].replace('./', ''), line=int(r['line']), column=int(r['column']), ) for r in results ] results = settings.process_errors(results, paths) if results: return SanityFailure(self.name, messages=results, python_version=python_version) return SanitySuccess(self.name, python_version=python_version)
def command_sanity_yamllint(args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: SanityResult """ test = 'yamllint' paths = sorted(i.path for i in targets.include if os.path.splitext(i.path)[1] in ('.yml', '.yaml')) if not paths: return SanitySkipped(test) cmd = [ 'yamllint', '--format', 'parsable', ] + paths try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySkipped(test) pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): \[(?P<level>warning|error)\] (?P<message>.*)$' results = [ re.search(pattern, line).groupdict() for line in stdout.splitlines() ] results = [ SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), level=r['level'], ) for r in results ] if results: return SanityFailure(test, messages=results) return SanitySuccess(test)
def test(self, args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str :rtype: SanityResult """ with open('test/sanity/ansible-doc/skip.txt', 'r') as skip_fd: skip_modules = set(skip_fd.read().splitlines()) modules = sorted( set(m for i in targets.include_external for m in i.modules) - set(m for i in targets.exclude_external for m in i.modules) - skip_modules) if not modules: return SanitySkipped(self.name, python_version=python_version) # ansible-doc fails due to async syntax errors on Python 3.7 currently if python_version == '3.7': return SanitySkipped(self.name, python_version=python_version) env = ansible_environment(args, color=False) cmd = ['ansible-doc'] + modules try: stdout, stderr = intercept_command(args, cmd, target_name='ansible-doc', env=env, capture=True, python_version=python_version) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if status: summary = u'%s' % SubprocessError( cmd=cmd, status=status, stderr=stderr) return SanityFailure(self.name, summary=summary, python_version=python_version) if stdout: display.info(stdout.strip(), verbosity=3) if stderr: summary = u'Output on stderr from ansible-doc is considered an error.\n\n%s' % SubprocessError( cmd, stderr=stderr) return SanityFailure(self.name, summary=summary, python_version=python_version) return SanitySuccess(self.name, python_version=python_version)
def test(self, args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str :rtype: TestResult """ ignore_file = os.path.join(SANITY_ROOT, 'rstcheck', 'ignore-substitutions.txt') ignore_substitutions = sorted(set(read_lines_without_comments(ignore_file, remove_blank_lines=True))) settings = self.load_processor(args) paths = [target.path for target in targets.include] cmd = [ find_python(python_version), '-m', 'rstcheck', '--report', 'warning', '--ignore-substitutions', ','.join(ignore_substitutions), ] + paths try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stdout: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySuccess(self.name) pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+): \((?P<level>INFO|WARNING|ERROR|SEVERE)/[0-4]\) (?P<message>.*)$' results = parse_to_list_of_dict(pattern, stderr) results = [SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=0, level=r['level'], ) for r in results] settings.process_errors(results, paths) if results: return SanityFailure(self.name, messages=results) return SanitySuccess(self.name)
def command_sanity_rstcheck(args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: SanityResult """ test = 'rstcheck' with open('test/sanity/rstcheck/ignore-substitutions.txt', 'r') as ignore_fd: ignore_substitutions = sorted(set(ignore_fd.read().splitlines())) paths = sorted(i.path for i in targets.include if os.path.splitext(i.path)[1] in ('.rst',)) if not paths: return SanitySkipped(test) cmd = [ 'rstcheck', '--report', 'warning', '--ignore-substitutions', ','.join(ignore_substitutions), ] + paths try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stdout: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySkipped(test) pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+): \((?P<level>INFO|WARNING|ERROR|SEVERE)/[0-4]\) (?P<message>.*)$' results = [parse_to_dict(pattern, line) for line in stderr.splitlines()] results = [SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=0, level=r['level'], ) for r in results] if results: return SanityFailure(test, messages=results) return SanitySuccess(test)
def command_sanity_ansible_doc(args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str :rtype: SanityResult """ test = 'ansible-doc' with open('test/sanity/ansible-doc/skip.txt', 'r') as skip_fd: skip_modules = set(skip_fd.read().splitlines()) modules = sorted( set(m for i in targets.include_external for m in i.modules) - set(m for i in targets.exclude_external for m in i.modules) - skip_modules) if not modules: return SanitySkipped(test, python_version=python_version) env = ansible_environment(args) cmd = ['ansible-doc'] + modules try: stdout, stderr = intercept_command(args, cmd, env=env, capture=True, python_version=python_version) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if status: summary = str(SubprocessError(cmd=cmd, status=status, stderr=stderr)) return SanityFailure(test, summary=summary, python_version=python_version) if stdout: display.info(stdout.strip(), verbosity=3) if stderr: summary = 'Output on stderr from ansible-doc is considered an error.\n\n%s' % SubprocessError( cmd, stderr=stderr) return SanityFailure(test, summary=summary, python_version=python_version) return SanitySuccess(test, python_version=python_version)
def test_paths(args, paths): """ :type args: SanityConfig :type paths: list[str] :rtype: list[SanityMessage] """ cmd = [ args.python_executable, #'test/sanity/yamllint/yamllinter.py', os.path.join( os.path.dirname(ansible_test.__file__), 'lib/sanity/yamllint/yamllinter.py', ) ] data = '\n'.join(paths) display.info(data, verbosity=4) try: stdout, stderr = run_command(args, cmd, data=data, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return [] results = json.loads(stdout)['messages'] results = [ SanityMessage( code=r['code'], message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), level=r['level'], ) for r in results ] return results
def test(self, args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str :rtype: TestResult """ with open('test/sanity/ansible-doc/skip.txt', 'r') as skip_fd: skip_modules = set(skip_fd.read().splitlines()) modules = sorted(set(m for i in targets.include_external for m in i.modules) - set(m for i in targets.exclude_external for m in i.modules) - skip_modules) if not modules: return SanitySkipped(self.name, python_version=python_version) module_paths = dict((t.module, t.path) for t in targets.targets if t.module) env = ansible_environment(args, color=False) cmd = ['ansible-doc'] + modules try: stdout, stderr = intercept_command(args, cmd, target_name='ansible-doc', env=env, capture=True, python_version=python_version) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: errors = stderr.strip().splitlines() messages = [self.parse_error(e, module_paths) for e in errors] if messages and all(messages): return SanityFailure(self.name, messages=messages, python_version=python_version) if status: summary = u'%s' % SubprocessError(cmd=cmd, status=status, stderr=stderr) return SanityFailure(self.name, summary=summary, python_version=python_version) if stdout: display.info(stdout.strip(), verbosity=3) if stderr: summary = u'Output on stderr from ansible-doc is considered an error.\n\n%s' % SubprocessError(cmd, stderr=stderr) return SanityFailure(self.name, summary=summary, python_version=python_version) return SanitySuccess(self.name, python_version=python_version)
def test_paths(args, paths, python): """ :type args: SanityConfig :type paths: list[str] :type python: str :rtype: list[SanityMessage] """ cmd = [ python, os.path.join(SANITY_ROOT, 'yamllint', 'yamllinter.py'), ] data = '\n'.join(paths) display.info(data, verbosity=4) try: stdout, stderr = run_command(args, cmd, data=data, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return [] results = json.loads(stdout)['messages'] results = [ SanityMessage( code=r['code'], message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), level=r['level'], ) for r in results ] return results
def pylint(self, args, context, paths): """ :type args: SanityConfig :param context: str :param paths: list[str] :return: list[dict[str, str]] """ rcfile = 'test/sanity/pylint/config/%s' % context if not os.path.exists(rcfile): rcfile = 'test/sanity/pylint/config/default' cmd = [ 'python%s' % args.python_version, find_executable('pylint'), '--jobs', '0', '--reports', 'n', '--max-line-length', '160', '--rcfile', rcfile, '--output-format', 'json', ] + paths env = ansible_environment(args) if paths: try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status >= 32: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) else: stdout = None if not args.explain and stdout: messages = json.loads(stdout) else: messages = [] return messages
def test_paths(self, args, paths): """ :type args: SanityConfig :type paths: list[str] :rtype: list[SanityMessage] """ cmd = [ 'python%s' % args.python_version, 'test/sanity/yamllint/yamllinter.py', ] + paths try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return [] results = json.loads(stdout)['messages'] results = [ SanityMessage( code=r['code'], message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), level=r['level'], ) for r in results ] return results
def test(self, args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: TestResult """ skip_paths = read_lines_without_comments(PEP8_SKIP_PATH) legacy_paths = read_lines_without_comments(PEP8_LEGACY_PATH) legacy_ignore_file = 'test/sanity/pep8/legacy-ignore.txt' legacy_ignore = set(read_lines_without_comments(legacy_ignore_file, remove_blank_lines=True)) current_ignore_file = 'test/sanity/pep8/current-ignore.txt' current_ignore = sorted(read_lines_without_comments(current_ignore_file, remove_blank_lines=True)) skip_paths_set = set(skip_paths) legacy_paths_set = set(legacy_paths) paths = sorted(i.path for i in targets.include if (os.path.splitext(i.path)[1] == '.py' or i.path.startswith('bin/')) and i.path not in skip_paths_set) cmd = [ args.python_executable, '-m', 'pycodestyle', '--max-line-length', '160', '--config', '/dev/null', '--ignore', ','.join(sorted(current_ignore)), ] + paths if paths: try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) else: stdout = None if args.explain: return SanitySuccess(self.name) if stdout: pattern = '^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<code>[WE][0-9]{3}) (?P<message>.*)$' results = [re.search(pattern, line).groupdict() for line in stdout.splitlines()] else: results = [] results = [SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), level='warning' if r['code'].startswith('W') else 'error', code=r['code'], ) for r in results] failed_result_paths = set([result.path for result in results]) used_paths = set(paths) errors = [] summary = {} line = 0 for path in legacy_paths: line += 1 if not path: continue if not os.path.exists(path): # Keep files out of the list which no longer exist in the repo. errors.append(SanityMessage( code='A101', message='Remove "%s" since it does not exist' % path, path=PEP8_LEGACY_PATH, line=line, column=1, confidence=calculate_best_confidence(((PEP8_LEGACY_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) if path in used_paths and path not in failed_result_paths: # Keep files out of the list which no longer require the relaxed rule set. errors.append(SanityMessage( code='A201', message='Remove "%s" since it passes the current rule set' % path, path=PEP8_LEGACY_PATH, line=line, column=1, confidence=calculate_best_confidence(((PEP8_LEGACY_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) line = 0 for path in skip_paths: line += 1 if not path: continue if not os.path.exists(path): # Keep files out of the list which no longer exist in the repo. errors.append(SanityMessage( code='A101', message='Remove "%s" since it does not exist' % path, path=PEP8_SKIP_PATH, line=line, column=1, confidence=calculate_best_confidence(((PEP8_SKIP_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) for result in results: if result.path in legacy_paths_set and result.code in legacy_ignore: # Files on the legacy list are permitted to have errors on the legacy ignore list. # However, we want to report on their existence to track progress towards eliminating these exceptions. display.info('PEP 8: %s (legacy)' % result, verbosity=3) key = '%s %s' % (result.code, re.sub('[0-9]+', 'NNN', result.message)) if key not in summary: summary[key] = 0 summary[key] += 1 else: # Files not on the legacy list and errors not on the legacy ignore list are PEP 8 policy errors. errors.append(result) if summary: lines = [] count = 0 for key in sorted(summary): count += summary[key] lines.append('PEP 8: %5d %s' % (summary[key], key)) display.info('PEP 8: There were %d different legacy issues found (%d total):' % (len(summary), count), verbosity=1) display.info('PEP 8: Count Code Message', verbosity=1) for line in lines: display.info(line, verbosity=1) if errors: return SanityFailure(self.name, messages=errors) return SanitySuccess(self.name)
def test(self, args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str :rtype: TestResult """ #skip_file = 'test/sanity/import/skip.txt' skip_file = os.path.join( os.path.dirname(ansible_test.__file__), 'lib/sanity/import/skip.txt' ) skip_paths = read_lines_without_comments(skip_file, remove_blank_lines=True) skip_paths_set = set(skip_paths) paths = sorted( i.path for i in targets.include if os.path.splitext(i.path)[1] == '.py' and (i.path.startswith('lib/ansible/modules/') or i.path.startswith('lib/ansible/module_utils/')) and i.path not in skip_paths_set ) if not paths: return SanitySkipped(self.name, python_version=python_version) env = ansible_environment(args, color=False) # create a clean virtual environment to minimize the available imports beyond the python standard library virtual_environment_path = os.path.abspath('test/runner/.tox/minimal-py%s' % python_version.replace('.', '')) virtual_environment_bin = os.path.join(virtual_environment_path, 'bin') remove_tree(virtual_environment_path) python = find_python(python_version) cmd = [python, '-m', 'virtualenv', virtual_environment_path, '--python', python, '--no-setuptools', '--no-wheel'] if not args.coverage: cmd.append('--no-pip') run_command(args, cmd, capture=True) # add the importer to our virtual environment so it can be accessed through the coverage injector importer_path = os.path.join(virtual_environment_bin, 'importer.py') if not args.explain: os.symlink(os.path.abspath('test/sanity/import/importer.py'), importer_path) # create a minimal python library python_path = os.path.abspath('test/runner/.tox/import/lib') ansible_path = os.path.join(python_path, 'ansible') ansible_init = os.path.join(ansible_path, '__init__.py') ansible_link = os.path.join(ansible_path, 'module_utils') if not args.explain: make_dirs(ansible_path) with open(ansible_init, 'w'): pass if not os.path.exists(ansible_link): os.symlink('../../../../../../lib/ansible/module_utils', ansible_link) # activate the virtual environment env['PATH'] = '%s:%s' % (virtual_environment_bin, env['PATH']) env['PYTHONPATH'] = python_path # make sure coverage is available in the virtual environment if needed if args.coverage: run_command(args, generate_pip_install(['pip'], 'sanity.import', packages=['setuptools']), env=env) run_command(args, generate_pip_install(['pip'], 'sanity.import', packages=['coverage']), env=env) run_command(args, ['pip', 'uninstall', '--disable-pip-version-check', '-y', 'setuptools'], env=env) run_command(args, ['pip', 'uninstall', '--disable-pip-version-check', '-y', 'pip'], env=env) cmd = ['importer.py'] data = '\n'.join(paths) display.info(data, verbosity=4) results = [] virtualenv_python = os.path.join(virtual_environment_bin, 'python') try: stdout, stderr = intercept_command(args, cmd, self.name, env, capture=True, data=data, python_version=python_version, virtualenv=virtualenv_python) if stdout or stderr: raise SubprocessError(cmd, stdout=stdout, stderr=stderr) except SubprocessError as ex: if ex.status != 10 or ex.stderr or not ex.stdout: raise pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<message>.*)$' results = parse_to_list_of_dict(pattern, ex.stdout) results = [SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), ) for r in results] results = [result for result in results if result.path not in skip_paths_set] if results: return SanityFailure(self.name, messages=results, python_version=python_version) return SanitySuccess(self.name, python_version=python_version)
def test(self, args, targets, python_version): """ :type args: SanityConfig :type targets: SanityTargets :type python_version: str :rtype: SanityResult """ with open('test/sanity/import/skip.txt', 'r') as skip_fd: skip_paths = skip_fd.read().splitlines() skip_paths_set = set(skip_paths) paths = sorted( i.path for i in targets.include if os.path.splitext(i.path)[1] == '.py' and (i.path.startswith('lib/ansible/modules/') or i.path.startswith( 'lib/ansible/module_utils/')) and i.path not in skip_paths_set) if not paths: return SanitySkipped(self.name, python_version=python_version) env = ansible_environment(args, color=False) # create a clean virtual environment to minimize the available imports beyond the python standard library virtual_environment_path = os.path.abspath( 'test/runner/.tox/minimal-py%s' % python_version.replace('.', '')) virtual_environment_bin = os.path.join(virtual_environment_path, 'bin') remove_tree(virtual_environment_path) cmd = [ 'virtualenv', virtual_environment_path, '--python', 'python%s' % python_version, '--no-setuptools', '--no-wheel' ] if not args.coverage: cmd.append('--no-pip') run_command(args, cmd, capture=True) # add the importer to our virtual environment so it can be accessed through the coverage injector importer_path = os.path.join(virtual_environment_bin, 'importer.py') if not args.explain: os.symlink(os.path.abspath('test/runner/importer.py'), importer_path) # activate the virtual environment env['PATH'] = '%s:%s' % (virtual_environment_bin, env['PATH']) env['PYTHONPATH'] = os.path.abspath('test/runner/import/lib') # make sure coverage is available in the virtual environment if needed if args.coverage: run_command(args, generate_pip_install('pip', 'sanity.import', packages=['setuptools']), env=env) run_command(args, generate_pip_install('pip', 'sanity.import', packages=['coverage']), env=env) run_command(args, [ 'pip', 'uninstall', '--disable-pip-version-check', '-y', 'setuptools' ], env=env) run_command(args, [ 'pip', 'uninstall', '--disable-pip-version-check', '-y', 'pip' ], env=env) cmd = ['importer.py'] + paths results = [] try: stdout, stderr = intercept_command(args, cmd, target_name=self.name, env=env, capture=True, python_version=python_version, path=env['PATH']) if stdout or stderr: raise SubprocessError(cmd, stdout=stdout, stderr=stderr) except SubprocessError as ex: if ex.status != 10 or ex.stderr or not ex.stdout: raise pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<message>.*)$' results = [ re.search(pattern, line).groupdict() for line in ex.stdout.splitlines() ] results = [ SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), ) for r in results ] results = [ result for result in results if result.path not in skip_paths ] if results: return SanityFailure(self.name, messages=results, python_version=python_version) return SanitySuccess(self.name, python_version=python_version)
def command_sanity_pep8(args, targets): """ :type args: SanityConfig :type targets: SanityTargets """ skip_path = 'test/sanity/pep8/skip.txt' legacy_path = 'test/sanity/pep8/legacy-files.txt' with open(skip_path, 'r') as skip_fd: skip_paths = set(skip_fd.read().splitlines()) with open(legacy_path, 'r') as legacy_fd: legacy_paths = set(legacy_fd.read().splitlines()) with open('test/sanity/pep8/legacy-ignore.txt', 'r') as ignore_fd: legacy_ignore = set(ignore_fd.read().splitlines()) with open('test/sanity/pep8/current-ignore.txt', 'r') as ignore_fd: current_ignore = sorted(ignore_fd.read().splitlines()) paths = sorted( i.path for i in targets.include if os.path.splitext(i.path)[1] == '.py' and i.path not in skip_paths) if not paths: display.info('No tests applicable.', verbosity=1) return cmd = [ 'pep8', '--max-line-length', '160', '--config', '/dev/null', '--ignore', ','.join(sorted(current_ignore)), ] + paths try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr) pattern = '^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<code>[A-Z0-9]{4}) (?P<message>.*)$' results = [ re.search(pattern, line).groupdict() for line in stdout.splitlines() ] for result in results: for key in 'line', 'column': result[key] = int(result[key]) failed_result_paths = set([result['path'] for result in results]) passed_legacy_paths = set([ path for path in paths if path in legacy_paths and path not in failed_result_paths ]) errors = [] summary = {} for path in sorted(passed_legacy_paths): # Keep files out of the list which no longer require the relaxed rule set. errors.append( 'PEP 8: %s: Passes current rule set. Remove from legacy list (%s).' % (path, legacy_path)) for path in sorted(skip_paths): if not os.path.exists(path): # Keep files out of the list which no longer exist in the repo. errors.append( 'PEP 8: %s: Does not exist. Remove from skip list (%s).' % (path, skip_path)) for path in sorted(legacy_paths): if not os.path.exists(path): # Keep files out of the list which no longer exist in the repo. errors.append( 'PEP 8: %s: Does not exist. Remove from legacy list (%s).' % (path, legacy_path)) for result in results: path = result['path'] line = result['line'] column = result['column'] code = result['code'] message = result['message'] msg = 'PEP 8: %s:%s:%s: %s %s' % (path, line, column, code, message) if path in legacy_paths: msg += ' (legacy)' else: msg += ' (current)' if path in legacy_paths and code in legacy_ignore: # Files on the legacy list are permitted to have errors on the legacy ignore list. # However, we want to report on their existence to track progress towards eliminating these exceptions. display.info(msg, verbosity=3) key = '%s %s' % (code, re.sub('[0-9]+', 'NNN', message)) if key not in summary: summary[key] = 0 summary[key] += 1 else: # Files not on the legacy list and errors not on the legacy ignore list are PEP 8 policy errors. errors.append(msg) for error in errors: display.error(error) if summary: lines = [] count = 0 for key in sorted(summary): count += summary[key] lines.append('PEP 8: %5d %s' % (summary[key], key)) display.info( 'PEP 8: There were %d different legacy issues found (%d total):' % (len(summary), count), verbosity=1) display.info('PEP 8: Count Code Message', verbosity=1) for line in lines: display.info(line, verbosity=1) if errors: raise ApplicationError( 'PEP 8: There are %d issues which need to be resolved.' % len(errors))
def command_sanity_pylint(args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: SanityResult """ test = 'pylint' with open(PYLINT_SKIP_PATH, 'r') as skip_fd: skip_paths = skip_fd.read().splitlines() with open('test/sanity/pylint/disable.txt', 'r') as disable_fd: disable = set(disable_fd.read().splitlines()) skip_paths_set = set(skip_paths) paths = sorted(i.path for i in targets.include if os.path.splitext(i.path)[1] == '.py' and i.path not in skip_paths_set) if not paths: return SanitySkipped(test) cmd = [ 'pylint', '--jobs', '0', '--reports', 'n', '--max-line-length', '160', '--rcfile', '/dev/null', '--output-format', 'json', '--disable', ','.join(sorted(disable)), ] + paths env = ansible_environment(args) try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status >= 32: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySkipped(test) if stdout: messages = json.loads(stdout) else: messages = [] errors = [SanityMessage( message=m['message'], path=m['path'], line=int(m['line']), column=int(m['column']), level=m['type'], code=m['symbol'], ) for m in messages] line = 0 for path in skip_paths: line += 1 if not os.path.exists(path): # Keep files out of the list which no longer exist in the repo. errors.append(SanityMessage( code='A101', message='Remove "%s" since it does not exist' % path, path=PYLINT_SKIP_PATH, line=line, column=1, confidence=calculate_best_confidence(((PYLINT_SKIP_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) if errors: return SanityFailure(test, messages=errors) return SanitySuccess(test)
def command_sanity_validate_modules(args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: SanityResult """ test = 'validate-modules' env = ansible_environment(args, color=False) paths = [deepest_path(i.path, 'lib/ansible/modules/') for i in targets.include_external] paths = sorted(set(p for p in paths if p)) if not paths: return SanitySkipped(test) cmd = [ 'test/sanity/validate-modules/validate-modules', '--format', 'json', ] + paths with open('test/sanity/validate-modules/skip.txt', 'r') as skip_fd: skip_paths = skip_fd.read().splitlines() skip_paths += [e.path for e in targets.exclude_external] if skip_paths: cmd += ['--exclude', '^(%s)' % '|'.join(skip_paths)] if args.base_branch: cmd.extend([ '--base-branch', args.base_branch, ]) else: display.warning('Cannot perform module comparison against the base branch. Base branch not detected when running locally.') try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status not in (0, 3): raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySkipped(test) messages = json.loads(stdout) results = [] for filename in messages: output = messages[filename] for item in output['errors']: results.append(SanityMessage( path=filename, line=int(item['line']) if 'line' in item else 0, column=int(item['column']) if 'column' in item else 0, level='error', code='E%s' % item['code'], message=item['msg'], )) if results: return SanityFailure(test, messages=results) return SanitySuccess(test)
def test(self, args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: TestResult """ if args.python_version in UNSUPPORTED_PYTHON_VERSIONS: display.warning( 'Skipping validate-modules on unsupported Python version %s.' % args.python_version) return SanitySkipped(self.name) skip_paths = read_lines_without_comments(VALIDATE_SKIP_PATH, optional=True) skip_paths_set = set(skip_paths) env = ansible_environment(args, color=False) paths = sorted([ i.path for i in targets.include if i.module and i.path not in skip_paths_set ]) if not paths: return SanitySkipped(self.name) cmd = [ args.python_executable, os.path.join(INSTALL_ROOT, 'test/sanity/validate-modules/validate-modules'), '--format', 'json', '--arg-spec', ] + paths invalid_ignores = [] ignore_entries = read_lines_without_comments(VALIDATE_IGNORE_PATH, optional=True) ignore = collections.defaultdict( dict) # type: t.Dict[str, t.Dict[str, int]] line = 0 for ignore_entry in ignore_entries: line += 1 if not ignore_entry: continue if ' ' not in ignore_entry: invalid_ignores.append((line, 'Invalid syntax')) continue path, code = ignore_entry.split(' ', 1) ignore[path][code] = line if args.base_branch: cmd.extend([ '--base-branch', args.base_branch, ]) else: display.warning( 'Cannot perform module comparison against the base branch. Base branch not detected when running locally.' ) try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status not in (0, 3): raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySuccess(self.name) messages = json.loads(stdout) errors = [] for filename in messages: output = messages[filename] for item in output['errors']: errors.append( SanityMessage( path=filename, line=int(item['line']) if 'line' in item else 0, column=int(item['column']) if 'column' in item else 0, level='error', code='E%s' % item['code'], message=item['msg'], )) filtered = [] for error in errors: if error.code in ignore[error.path]: ignore[error.path][ error. code] = 0 # error ignored, clear line number of ignore entry to track usage else: filtered.append(error) # error not ignored errors = filtered for invalid_ignore in invalid_ignores: errors.append( SanityMessage( code='A201', message=invalid_ignore[1], path=VALIDATE_IGNORE_PATH, line=invalid_ignore[0], column=1, confidence=calculate_confidence(VALIDATE_IGNORE_PATH, line, args.metadata) if args.metadata.changes else None, )) line = 0 for path in skip_paths: line += 1 if not path: continue if not os.path.exists(path): # Keep files out of the list which no longer exist in the repo. errors.append( SanityMessage( code='A101', message='Remove "%s" since it does not exist' % path, path=VALIDATE_SKIP_PATH, line=line, column=1, confidence=calculate_best_confidence( ((VALIDATE_SKIP_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) for path in sorted(ignore.keys()): if os.path.exists(path): continue for line in sorted(ignore[path].values()): # Keep files out of the list which no longer exist in the repo. errors.append( SanityMessage( code='A101', message='Remove "%s" since it does not exist' % path, path=VALIDATE_IGNORE_PATH, line=line, column=1, confidence=calculate_best_confidence( ((VALIDATE_IGNORE_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) for path in paths: if path not in ignore: continue for code in ignore[path]: line = ignore[path][code] if not line: continue errors.append( SanityMessage( code='A102', message='Remove since "%s" passes "%s" test' % (path, code), path=VALIDATE_IGNORE_PATH, line=line, column=1, confidence=calculate_best_confidence( ((VALIDATE_IGNORE_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) if errors: return SanityFailure(self.name, messages=errors) return SanitySuccess(self.name)
def compile_version(args, python_version, include, exclude): """ :type args: CompileConfig :type python_version: str :type include: tuple[CompletionTarget] :type exclude: tuple[CompletionTarget] :rtype: TestResult """ command = 'compile' test = '' # optional list of regex patterns to exclude from tests skip_file = 'test/compile/python%s-skip.txt' % python_version if os.path.exists(skip_file): with open(skip_file, 'r') as skip_fd: skip_paths = skip_fd.read().splitlines() else: skip_paths = [] # augment file exclusions skip_paths += [e.path for e in exclude] skip_paths = sorted(skip_paths) python = 'python%s' % python_version cmd = [python, 'test/compile/compile.py'] if skip_paths: cmd += ['-x', '|'.join(skip_paths)] cmd += [target.path if target.path == '.' else './%s' % target.path for target in include] try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return TestSkipped(command, test, python_version=python_version) pattern = r'^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<message>.*)$' results = [re.search(pattern, line).groupdict() for line in stdout.splitlines()] results = [TestMessage( message=r['message'], path=r['path'].replace('./', ''), line=int(r['line']), column=int(r['column']), ) for r in results] if results: return TestFailure(command, test, messages=results, python_version=python_version) return TestSuccess(command, test, python_version=python_version)
def test(self, args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: TestResult """ current_ignore_file = os.path.join( ANSIBLE_ROOT, 'test/sanity/pep8/current-ignore.txt') current_ignore = sorted( read_lines_without_comments(current_ignore_file, remove_blank_lines=True)) settings = self.load_settings(args, 'A100') paths = sorted(i.path for i in targets.include if os.path.splitext(i.path)[1] == '.py' or i.path.startswith('bin/')) paths = settings.filter_skipped_paths(paths) cmd = [ args.python_executable, '-m', 'pycodestyle', '--max-line-length', '160', '--config', '/dev/null', '--ignore', ','.join(sorted(current_ignore)), ] + paths if paths: try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) else: stdout = None if args.explain: return SanitySuccess(self.name) if stdout: pattern = '^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<code>[WE][0-9]{3}) (?P<message>.*)$' results = parse_to_list_of_dict(pattern, stdout) else: results = [] results = [ SanityMessage( message=r['message'], path=r['path'], line=int(r['line']), column=int(r['column']), level='warning' if r['code'].startswith('W') else 'error', code=r['code'], ) for r in results ] errors = settings.process_errors(results, paths) if errors: return SanityFailure(self.name, messages=errors) return SanitySuccess(self.name)
def test(self, args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: TestResult """ if self.path.endswith('.py'): cmd = [args.python_executable, self.path] else: cmd = [self.path] env = ansible_environment(args, color=False) pattern = None data = None if self.config: output = self.config.get('output') extensions = self.config.get('extensions') prefixes = self.config.get('prefixes') files = self.config.get('files') always = self.config.get('always') text = self.config.get('text') if output == 'path-line-column-message': pattern = '^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<message>.*)$' elif output == 'path-message': pattern = '^(?P<path>[^:]*): (?P<message>.*)$' else: pattern = ApplicationError('Unsupported output type: %s' % output) paths = sorted(i.path for i in targets.include) if always: paths = [] # short-term work-around for paths being str instead of unicode on python 2.x if sys.version_info[0] == 2: paths = [p.decode('utf-8') for p in paths] if text is not None: if text: paths = [p for p in paths if not is_binary_file(p)] else: paths = [p for p in paths if is_binary_file(p)] if extensions: paths = [ p for p in paths if os.path.splitext(p)[1] in extensions or ( p.startswith('bin/') and '.py' in extensions) ] if prefixes: paths = [ p for p in paths if any( p.startswith(pre) for pre in prefixes) ] if files: paths = [p for p in paths if os.path.basename(p) in files] if not paths and not always: return SanitySkipped(self.name) data = '\n'.join(paths) if data: display.info(data, verbosity=4) try: stdout, stderr = run_command(args, cmd, data=data, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stdout and not stderr: if pattern: matches = [ parse_to_dict(pattern, line) for line in stdout.splitlines() ] messages = [ SanityMessage( message=m['message'], path=m['path'], line=int(m.get('line', 0)), column=int(m.get('column', 0)), ) for m in matches ] return SanityFailure(self.name, messages=messages) if stderr or status: summary = u'%s' % SubprocessError( cmd=cmd, status=status, stderr=stderr, stdout=stdout) return SanityFailure(self.name, summary=summary) return SanitySuccess(self.name)
def test(self, args, targets): """ :type args: SanityConfig :type targets: SanityTargets :rtype: TestResult """ skip_paths = read_lines_without_comments(PSLINT_SKIP_PATH) invalid_ignores = [] ignore_entries = read_lines_without_comments(PSLINT_IGNORE_PATH) ignore = collections.defaultdict(dict) line = 0 for ignore_entry in ignore_entries: line += 1 if not ignore_entry: continue if ' ' not in ignore_entry: invalid_ignores.append((line, 'Invalid syntax')) continue path, code = ignore_entry.split(' ', 1) if not os.path.exists(path): invalid_ignores.append( (line, 'Remove "%s" since it does not exist' % path)) continue ignore[path][code] = line paths = sorted( i.path for i in targets.include if os.path.splitext(i.path)[1] in ( '.ps1', '.psm1', '.psd1') and i.path not in skip_paths) if not paths: return SanitySkipped(self.name) if not find_executable('pwsh', required='warning'): return SanitySkipped(self.name) # Make sure requirements are installed before running sanity checks cmds = [['test/runner/requirements/sanity.ps1'], ['test/sanity/pslint/pslint.ps1'] + paths] for cmd in cmds: try: stdout, stderr = run_command(args, cmd, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) if args.explain: return SanitySuccess(self.name) severity = [ 'Information', 'Warning', 'Error', 'ParseError', ] cwd = os.getcwd() + '/' # replace unicode smart quotes and ellipsis with ascii versions stdout = re.sub(u'[\u2018\u2019]', "'", stdout) stdout = re.sub(u'[\u201c\u201d]', '"', stdout) stdout = re.sub(u'[\u2026]', '...', stdout) messages = json.loads(stdout) errors = [ SanityMessage( code=m['RuleName'], message=m['Message'], path=m['ScriptPath'].replace(cwd, ''), line=m['Line'] or 0, column=m['Column'] or 0, level=severity[m['Severity']], ) for m in messages ] line = 0 filtered = [] for error in errors: if error.code in ignore[error.path]: ignore[error.path][ error. code] = None # error ignored, clear line number of ignore entry to track usage else: filtered.append(error) # error not ignored errors = filtered for invalid_ignore in invalid_ignores: errors.append( SanityMessage( code='A201', message=invalid_ignore[1], path=PSLINT_IGNORE_PATH, line=invalid_ignore[0], column=1, confidence=calculate_confidence(PSLINT_IGNORE_PATH, line, args.metadata) if args.metadata.changes else None, )) for path in skip_paths: line += 1 if not path: continue if not os.path.exists(path): # Keep files out of the list which no longer exist in the repo. errors.append( SanityMessage( code='A101', message='Remove "%s" since it does not exist' % path, path=PSLINT_SKIP_PATH, line=line, column=1, confidence=calculate_best_confidence( ((PSLINT_SKIP_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) for path in paths: if path not in ignore: continue for code in ignore[path]: line = ignore[path][code] if not line: continue errors.append( SanityMessage( code='A102', message='Remove since "%s" passes "%s" test' % (path, code), path=PSLINT_IGNORE_PATH, line=line, column=1, confidence=calculate_best_confidence( ((PSLINT_IGNORE_PATH, line), (path, 0)), args.metadata) if args.metadata.changes else None, )) if errors: return SanityFailure(self.name, messages=errors) return SanitySuccess(self.name)
def pylint(self, args, context, paths): """ :type args: SanityConfig :type context: str :type paths: list[str] :rtype: list[dict[str, str]] """ rcfile = 'test/sanity/pylint/config/%s' % context if not os.path.exists(rcfile): rcfile = 'test/sanity/pylint/config/default' parser = configparser.SafeConfigParser() parser.read(rcfile) if parser.has_section('ansible-test'): config = dict(parser.items('ansible-test')) else: config = dict() disable_plugins = set( i.strip() for i in config.get('disable-plugins', '').split(',') if i) load_plugins = set(self.plugin_names) - disable_plugins cmd = [ args.python_executable, '-m', 'pylint', '--jobs', '0', '--reports', 'n', '--max-line-length', '160', '--rcfile', rcfile, '--output-format', 'json', '--load-plugins', ','.join(load_plugins), ] + paths env = ansible_environment(args) env['PYTHONPATH'] += '%s%s' % (os.pathsep, self.plugin_dir) if paths: try: stdout, stderr = run_command(args, cmd, env=env, capture=True) status = 0 except SubprocessError as ex: stdout = ex.stdout stderr = ex.stderr status = ex.status if stderr or status >= 32: raise SubprocessError(cmd=cmd, status=status, stderr=stderr, stdout=stdout) else: stdout = None if not args.explain and stdout: messages = json.loads(stdout) else: messages = [] return messages