From 3a2670e0fdb22939f38a9f08f038d2b3623e12df Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Thu, 27 Jul 2017 03:37:32 -0700 Subject: [PATCH] Allow specific __future__ imports in modules We do want to allow certain from __future__ imports in modules that make it easier to code compatible python2 and python3. Note that unicode_literals is specifically left out and should never be allowed. Now that python-3.4+ allows u"" there's no good reason to use unicode_literals. Also switch tables in the validate_modules documentation to simple table format --- .../dev_guide/testing_validate-modules.rst | 199 +++++++----------- test/sanity/validate-modules/main.py | 16 ++ 2 files changed, 95 insertions(+), 120 deletions(-) diff --git a/docs/docsite/rst/dev_guide/testing_validate-modules.rst b/docs/docsite/rst/dev_guide/testing_validate-modules.rst index 2499410487..31359e181f 100644 --- a/docs/docsite/rst/dev_guide/testing_validate-modules.rst +++ b/docs/docsite/rst/dev_guide/testing_validate-modules.rst @@ -57,127 +57,86 @@ Codes Errors ------ -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| code | sample message | -+=========+============================================================================================================================================+ -| **1xx** | **Locations** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 101 | Interpreter line is not ``#!/usr/bin/python`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 102 | Interpreter line is not ``#!powershell`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 103 | Did not find a call to ``main()`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 104 | Call to ``main()`` not the last line | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 105 | GPLv3 license header not found | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 106 | Import found before documentation variables. All imports must appear below ``DOCUMENTATION``/``EXAMPLES``/``RETURN``/``ANSIBLE_METADATA`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 107 | Imports should be directly below ``DOCUMENTATION``/``EXAMPLES``/``RETURN``/``ANSIBLE_METADATA`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| **2xx** | **Imports** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 201 | Did not find a ``module_utils`` import | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 203 | ``requests`` import found, should use ``ansible.module_utils.urls`` instead | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 204 | ``boto`` import found, new modules should use ``boto3`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 205 | ``sys.exit()`` call found. Should be ``exit_json``/``fail_json`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 206 | ``WANT_JSON`` not found in module | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 207 | ``REPLACER_WINDOWS`` not found in module | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 208 | ``module_utils`` imports should import specific components, not ``*`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| **3xx** | **Documentation** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 301 | No ``DOCUMENTATION`` provided | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 302 | ``DOCUMENTATION`` is not valid YAML | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 303 | ``DOCUMENTATION`` fragment missing | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 304 | Unknown ``DOCUMENTATION`` error | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 305 | Invalid ``DOCUMENTATION`` schema | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 306 | Module level ``version_added`` is not a valid version number | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 307 | Module level ``version_added`` is incorrect | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 308 | ``version_added`` for new option is not a valid version number | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 309 | ``version_added`` for new option is incorrect | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 310 | No ``EXAMPLES`` provided | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 311 | ``EXAMPLES`` is not valid YAML | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 312 | No ``RETURN`` documentation provided | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 313 | ``RETURN`` is not valid YAML | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 314 | No ``ANSIBLE_METADATA`` provided | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 315 | ``ANSIBLE_METADATA`` is not valid YAML | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 316 | Invalid ``ANSIBLE_METADATA`` schema | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 317 | option is marked as required but specifies a default. Arguments with a default should not be marked as required | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 318 | Module deprecated, but DOCUMENTATION.deprecated is missing | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 319 | ``RETURN`` fragments missing or invalid | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| **4xx** | **Syntax** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 401 | Python ``SyntaxError`` while parsing module | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 402 | Indentation contains tabs | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 403 | Type comparison using ``type()`` found. Use ``isinstance()`` instead | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| **5xx** | **Naming** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 501 | Official Ansible modules must have a ``.py`` extension for python modules or a ``.ps1`` for powershell modules | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 502 | Ansible module subdirectories must contain an ``__init__.py`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 503 | Missing python documentation file | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ +========= =================== + code sample message +--------- ------------------- + **1xx** **Locations** + 101 Interpreter line is not ``#!/usr/bin/python`` + 102 Interpreter line is not ``#!powershell`` + 103 Did not find a call to ``main()`` + 104 Call to ``main()`` not the last line + 105 GPLv3 license header not found + 106 Import found before documentation variables. All imports must appear below + ``DOCUMENTATION``/``EXAMPLES``/``RETURN``/``ANSIBLE_METADATA`` + 107 Imports should be directly below ``DOCUMENTATION``/``EXAMPLES``/``RETURN``/``ANSIBLE_METADATA`` + .. +--------- ------------------- + **2xx** **Imports** + 201 Did not find a ``module_utils`` import + 203 ``requests`` import found, should use ``ansible.module_utils.urls`` instead + 204 ``boto`` import found, new modules should use ``boto3`` + 205 ``sys.exit()`` call found. Should be ``exit_json``/``fail_json`` + 206 ``WANT_JSON`` not found in module + 207 ``REPLACER_WINDOWS`` not found in module + 208 ``module_utils`` imports should import specific components, not ``*`` + 209 Only the following ``from __future__`` imports are allowed: + ``absolute_import``, ``division``, and ``print_function``. + .. +--------- ------------------- + **3xx** **Documentation** + 301 No ``DOCUMENTATION`` provided + 302 ``DOCUMENTATION`` is not valid YAML + 303 ``DOCUMENTATION`` fragment missing + 304 Unknown ``DOCUMENTATION`` error + 305 Invalid ``DOCUMENTATION`` schema + 306 Module level ``version_added`` is not a valid version number + 307 Module level ``version_added`` is incorrect + 308 ``version_added`` for new option is not a valid version number + 309 ``version_added`` for new option is incorrect + 310 No ``EXAMPLES`` provided + 311 ``EXAMPLES`` is not valid YAML + 312 No ``RETURN`` documentation provided + 313 ``RETURN`` is not valid YAML + 314 No ``ANSIBLE_METADATA`` provided + 315 ``ANSIBLE_METADATA`` is not valid YAML + 316 Invalid ``ANSIBLE_METADATA`` schema + 317 option is marked as required but specifies a default. + Arguments with a default should not be marked as required + 318 Module deprecated, but DOCUMENTATION.deprecated is missing + 319 ``RETURN`` fragments missing or invalid + .. +--------- ------------------- + **4xx** **Syntax** + 401 Python ``SyntaxError`` while parsing module + 402 Indentation contains tabs + 403 Type comparison using ``type()`` found. Use ``isinstance()`` instead + .. +--------- ------------------- + **5xx** **Naming** + 501 Official Ansible modules must have a ``.py`` extension for python + modules or a ``.ps1`` for powershell modules + 502 Ansible module subdirectories must contain an ``__init__.py`` + 503 Missing python documentation file +========= =================== Warnings -------- -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| code | sample message | -+=========+============================================================================================================================================+ -| **1xx** | **Locations** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 107 | Imports should be directly below ``DOCUMENTATION``/``EXAMPLES``/``RETURN``/``ANSIBLE_METADATA`` for legacy modules | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| **2xx** | **Imports** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 208 | ``module_utils`` imports should import specific components for legacy module, not ``*`` | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 291 | Try/Except ``HAS_`` expression missing | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 292 | Did not find ``ansible.module_utils.basic`` import | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| **3xx** | **Documentation** | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 312 | No ``RETURN`` documentation provided for legacy module | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 391 | Unknown pre-existing ``DOCUMENTATION`` error | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ -| 392 | Pre-existing ``DOCUMENTATION`` fragment missing | -+---------+--------------------------------------------------------------------------------------------------------------------------------------------+ +========= =================== + code sample message +--------- ------------------- + **1xx** **Locations** + 107 Imports should be directly below ``DOCUMENTATION``/``EXAMPLES``/``RETURN``/``ANSIBLE_METADATA`` for legacy modules + .. +--------- ------------------- + **2xx** **Imports** + 208 ``module_utils`` imports should import specific components for legacy module, not ``*`` + 291 Try/Except ``HAS_`` expression missing + 292 Did not find ``ansible.module_utils.basic`` import + .. +--------- ------------------- + **3xx** **Documentation** + 312 No ``RETURN`` documentation provided for legacy module + 391 Unknown pre-existing ``DOCUMENTATION`` error + 392 Pre-existing ``DOCUMENTATION`` fragment missing +========= =================== diff --git a/test/sanity/validate-modules/main.py b/test/sanity/validate-modules/main.py index c9336e3bde..0810d6de53 100755 --- a/test/sanity/validate-modules/main.py +++ b/test/sanity/validate-modules/main.py @@ -225,6 +225,8 @@ class ModuleValidator(Validator): 'setup.ps1' )) + WHITELIST_FUTURE_IMPORTS = frozenset(('absolute_import', 'division', 'print_function')) + def __init__(self, path, analyze_arg_spec=False, base_branch=None, git_cache=None, reporter=None): super(ModuleValidator, self).__init__(reporter=reporter or Reporter()) @@ -558,6 +560,20 @@ class ModuleValidator(Validator): for child in self.ast.body: if isinstance(child, (ast.Import, ast.ImportFrom)): + if isinstance(child, ast.ImportFrom) and child.module == '__future__': + # allowed from __future__ imports + for future_import in child.names: + if future_import.name not in self.WHITELIST_FUTURE_IMPORTS: + self.reporter.error( + path=self.object_path, + code=209, + msg=('Only the following from __future__ imports are allowed: %s' + % ', '.join(self.WHITELIST_FUTURE_IMPORTS)), + line=child.lineno + ) + break + else: # for-else. If we didn't find a problem nad break out of the loop, then this is a legal import + continue import_lines.append(child.lineno) if child.lineno < min_doc_line: self.reporter.error(