diff --git a/test/sanity/code-smell/shebang.sh b/test/sanity/code-smell/shebang.sh index 6b1c07f0678..0bc77b818dc 100755 --- a/test/sanity/code-smell/shebang.sh +++ b/test/sanity/code-smell/shebang.sh @@ -7,7 +7,6 @@ grep '^#!' -rIn . \ -e '^\./lib/ansible/modules/' \ -e '^\./test/integration/targets/[^/]*/library/[^/]*:#!powershell$' \ -e '^\./test/integration/targets/[^/]*/library/[^/]*:#!/usr/bin/python$' \ - -e '^\./test/sanity/validate-modules/validate-modules:#!/usr/bin/env python2$' \ -e '^\./hacking/cherrypick.py:#!/usr/bin/env python3$' \ -e ':#!/bin/sh$' \ -e ':#!/bin/bash( -[eux]|$)' \ diff --git a/test/sanity/validate-modules/schema.py b/test/sanity/validate-modules/schema.py index 155b09c6ae3..ffef4922a84 100644 --- a/test/sanity/validate-modules/schema.py +++ b/test/sanity/validate-modules/schema.py @@ -17,52 +17,62 @@ # along with this program. If not, see . from voluptuous import PREVENT_EXTRA, Any, Required, Schema +from ansible.module_utils.six import string_types +list_string_types = list(string_types) suboption_schema = Schema( { - Required('description'): Any(basestring, [basestring]), + Required('description'): Any(list_string_types, *string_types), 'required': bool, 'choices': list, - 'aliases': Any(basestring, list), - 'version_added': Any(basestring, float), - 'default': Any(None, basestring, float, int, bool, list, dict), + 'aliases': Any(list, *string_types), + 'version_added': Any(float, *string_types), + 'default': Any(None, float, int, bool, list, dict, *string_types), # Note: Types are strings, not literal bools, such as True or False 'type': Any(None, "bool") }, extra=PREVENT_EXTRA ) +# This generates list of dicts with keys from string_types and suboption_schema value +# for example in Python 3: {str: suboption_schema} +list_dict_suboption_schema = [{str_type: suboption_schema} for str_type in string_types] + option_schema = Schema( { - Required('description'): Any(basestring, [basestring]), + Required('description'): Any(list_string_types, *string_types), 'required': bool, 'choices': list, - 'aliases': Any(basestring, list), - 'version_added': Any(basestring, float), - 'default': Any(None, basestring, float, int, bool, list, dict), - 'suboptions': Any(None, {basestring: suboption_schema,}), + 'aliases': Any(list, *string_types), + 'version_added': Any(float, *string_types), + 'default': Any(None, float, int, bool, list, dict, *string_types), + 'suboptions': Any(None, *list_dict_suboption_schema), # Note: Types are strings, not literal bools, such as True or False 'type': Any(None, "bool") }, extra=PREVENT_EXTRA ) +# This generates list of dicts with keys from string_types and option_schema value +# for example in Python 3: {str: option_schema} +list_dict_option_schema = [{str_type: option_schema} for str_type in string_types] + def doc_schema(module_name): if module_name.startswith('_'): module_name = module_name[1:] return Schema( { Required('module'): module_name, - 'deprecated': basestring, - Required('short_description'): basestring, - Required('description'): Any(basestring, [basestring]), - Required('version_added'): Any(basestring, float), - Required('author'): Any(None, basestring, [basestring]), - 'notes': Any(None, [basestring]), - 'requirements': [basestring], - 'todo': Any(None, basestring, [basestring]), - 'options': Any(None, {basestring: option_schema}), - 'extends_documentation_fragment': Any(basestring, [basestring]) + 'deprecated': Any(*string_types), + Required('short_description'): Any(*string_types), + Required('description'): Any(list_string_types, *string_types), + Required('version_added'): Any(float, *string_types), + Required('author'): Any(None, list_string_types, *string_types), + 'notes': Any(None, list_string_types), + 'requirements': list_string_types, + 'todo': Any(None, list_string_types, *string_types), + 'options': Any(None, *list_dict_option_schema), + 'extends_documentation_fragment': Any(list_string_types, *string_types) }, extra=PREVENT_EXTRA ) diff --git a/test/sanity/validate-modules/utils.py b/test/sanity/validate-modules/utils.py index dbda4938c7a..c997de64a69 100644 --- a/test/sanity/validate-modules/utils.py +++ b/test/sanity/validate-modules/utils.py @@ -19,8 +19,7 @@ import ast import sys -# We only use StringIO, since we cannot setattr on cStringIO -from StringIO import StringIO +from io import BytesIO, TextIOWrapper import yaml import yaml.reader @@ -55,10 +54,8 @@ class CaptureStd(): def __enter__(self): self.sys_stdout = sys.stdout self.sys_stderr = sys.stderr - sys.stdout = self.stdout = StringIO() - sys.stderr = self.stderr = StringIO() - setattr(sys.stdout, 'encoding', self.sys_stdout.encoding) - setattr(sys.stderr, 'encoding', self.sys_stderr.encoding) + sys.stdout = self.stdout = TextIOWrapper(BytesIO(), encoding=self.sys_stdout.encoding) + sys.stderr = self.stderr = TextIOWrapper(BytesIO(), encoding=self.sys_stderr.encoding) return self def __exit__(self, exc_type, exc_value, traceback): diff --git a/test/sanity/validate-modules/validate-modules b/test/sanity/validate-modules/validate-modules index 07e3089c57f..ee55e2a0589 100755 --- a/test/sanity/validate-modules/validate-modules +++ b/test/sanity/validate-modules/validate-modules @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2015 Matt Martz @@ -46,6 +46,16 @@ from schema import doc_schema, option_schema, metadata_schema from utils import CaptureStd, parse_yaml from voluptuous.humanize import humanize_error +from ansible.module_utils.six import PY3, with_metaclass + +if PY3: + # Because there is no ast.TryExcept in Python 3 ast module + TRY_EXCEPT = ast.Try + # REPLACER_WINDOWS from ansible.executor.module_common is byte + # string but we need unicode for Python 3 + REPLACER_WINDOWS = REPLACER_WINDOWS.decode('utf-8') +else: + TRY_EXCEPT = ast.TryExcept BLACKLIST_DIRS = frozenset(('.git', 'test', '.github', '.idea')) INDENT_REGEX = re.compile(r'([\t]*)') @@ -78,6 +88,7 @@ class ReporterEncoder(json.JSONEncoder): class Reporter(object): + @staticmethod @contextmanager def _output_handle(output): @@ -93,10 +104,10 @@ class Reporter(object): @staticmethod def _filter_out_ok(reports): - temp_reports = reports.copy() - for path, report in temp_reports.items(): - if not (report['errors'] or report['warnings']): - del temp_reports[path] + temp_reports = OrderedDict() + for path, report in reports.items(): + if report['errors'] or report['warnings']: + temp_reports[path] = report return temp_reports @@ -148,11 +159,10 @@ class Reporter(object): return 3 if sum(ret) else 0 -class Validator(object): +class Validator(with_metaclass(abc.ABCMeta, object)): """Validator instances are intended to be run on a single object. if you are scanning multiple objects for problems, you'll want to have a separate Validator for each one.""" - __metaclass__ = abc.ABCMeta def __init__(self): self.reset() @@ -352,7 +362,7 @@ class ModuleValidator(Validator): names = [] if isinstance(child, ast.Import): names.extend(child.names) - elif isinstance(child, ast.TryExcept): + elif isinstance(child, TRY_EXCEPT): bodies = child.body for handler in child.handlers: bodies.extend(handler.body) @@ -459,7 +469,7 @@ class ModuleValidator(Validator): for child in self.ast.body: found_try_except_import = False found_has = False - if isinstance(child, ast.TryExcept): + if isinstance(child, TRY_EXCEPT): bodies = child.body for handler in child.handlers: bodies.extend(handler.body) @@ -498,7 +508,7 @@ class ModuleValidator(Validator): 'line %d' % (child.lineno,)) )) break - elif isinstance(child, ast.TryExcept): + elif isinstance(child, TRY_EXCEPT): bodies = child.body for handler in child.handlers: bodies.extend(handler.body) @@ -970,7 +980,7 @@ def main(): reports.update(mv.report()) for root, dirs, files in os.walk(module): - basedir = root[len(module)+1:].split('/', 1)[0] + basedir = root[len(module) + 1:].split('/', 1)[0] if basedir in BLACKLIST_DIRS: continue for dirname in dirs: @@ -1027,7 +1037,7 @@ class GitCache(object): cmd = ['git'] + args p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() - return stdout.splitlines() + return stdout.decode('utf-8').splitlines() if __name__ == '__main__':