Increase scope of mypy sanity test (#84288)

* Increase scope of mypy sanity test

* Fix issues reported by mypy
pull/84291/head
Matt Clay 1 year ago committed by GitHub
parent 7501bbec20
commit 9d249432c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -9,6 +9,6 @@ from yaml import load
try: try:
from yaml import CSafeLoader as SafeLoader from yaml import CSafeLoader as SafeLoader
except ImportError: except ImportError:
from yaml import SafeLoader from yaml import SafeLoader # type: ignore[assignment]
json.dump(load(sys.stdin, Loader=SafeLoader), sys.stdout) json.dump(load(sys.stdin, Loader=SafeLoader), sys.stdout)

@ -12,16 +12,6 @@ from tokenize import COMMENT, TokenInfo
import astroid import astroid
# support pylint 2.x and 3.x -- remove when supporting only 3.x
try:
from pylint.interfaces import IAstroidChecker, ITokenChecker
except ImportError:
class IAstroidChecker:
"""Backwards compatibility for 2.x / 3.x support."""
class ITokenChecker:
"""Backwards compatibility for 2.x / 3.x support."""
try: try:
from pylint.checkers.utils import check_messages from pylint.checkers.utils import check_messages
except ImportError: except ImportError:
@ -151,7 +141,6 @@ class AnsibleDeprecatedChecker(BaseChecker):
has not passed or met the time for removal has not passed or met the time for removal
""" """
__implements__ = (IAstroidChecker,)
name = 'deprecated' name = 'deprecated'
msgs = MSGS msgs = MSGS
@ -296,8 +285,6 @@ class AnsibleDeprecatedCommentChecker(BaseTokenChecker):
has not passed or met the time for removal has not passed or met the time for removal
""" """
__implements__ = (ITokenChecker,)
name = 'deprecated-comment' name = 'deprecated-comment'
msgs = { msgs = {
'E9601': ("Deprecated core version (%r) found: %s", 'E9601': ("Deprecated core version (%r) found: %s",

@ -6,13 +6,6 @@ from __future__ import annotations
import astroid import astroid
# support pylint 2.x and 3.x -- remove when supporting only 3.x
try:
from pylint.interfaces import IAstroidChecker
except ImportError:
class IAstroidChecker:
"""Backwards compatibility for 2.x / 3.x support."""
try: try:
from pylint.checkers.utils import check_messages from pylint.checkers.utils import check_messages
except ImportError: except ImportError:
@ -38,7 +31,6 @@ class AnsibleStringFormatChecker(BaseChecker):
is valid and the arguments match the format string. is valid and the arguments match the format string.
""" """
__implements__ = (IAstroidChecker,)
name = 'string' name = 'string'
msgs = MSGS msgs = MSGS

@ -6,13 +6,6 @@ import typing as t
import astroid import astroid
# support pylint 2.x and 3.x -- remove when supporting only 3.x
try:
from pylint.interfaces import IAstroidChecker
except ImportError:
class IAstroidChecker:
"""Backwards compatibility for 2.x / 3.x support."""
from pylint.checkers import BaseChecker from pylint.checkers import BaseChecker
ANSIBLE_TEST_MODULES_PATH = os.environ['ANSIBLE_TEST_MODULES_PATH'] ANSIBLE_TEST_MODULES_PATH = os.environ['ANSIBLE_TEST_MODULES_PATH']
@ -63,7 +56,6 @@ def is_module_path(path): # type: (str) -> bool
class AnsibleUnwantedChecker(BaseChecker): class AnsibleUnwantedChecker(BaseChecker):
"""Checker for unwanted imports and functions.""" """Checker for unwanted imports and functions."""
__implements__ = (IAstroidChecker,)
name = 'unwanted' name = 'unwanted'

@ -65,7 +65,7 @@ def setup_collection_loader():
setup_collection_loader() setup_collection_loader()
from ansible import __version__ as ansible_version from ansible import __version__ as ansible_version
from ansible.executor.module_common import REPLACER_WINDOWS, NEW_STYLE_PYTHON_MODULE_RE from ansible.executor.module_common import REPLACER_WINDOWS as _REPLACER_WINDOWS, NEW_STYLE_PYTHON_MODULE_RE
from ansible.module_utils.common.collections import is_iterable from ansible.module_utils.common.collections import is_iterable
from ansible.module_utils.common.parameters import DEFAULT_TYPE_VALIDATORS from ansible.module_utils.common.parameters import DEFAULT_TYPE_VALIDATORS
from ansible.module_utils.compat.version import StrictVersion, LooseVersion from ansible.module_utils.compat.version import StrictVersion, LooseVersion
@ -90,7 +90,7 @@ from .utils import CaptureStd, NoArgsAnsibleModule, compare_unordered_lists, par
TRY_EXCEPT = ast.Try TRY_EXCEPT = ast.Try
# REPLACER_WINDOWS from ansible.executor.module_common is byte # REPLACER_WINDOWS from ansible.executor.module_common is byte
# string but we need unicode for Python 3 # string but we need unicode for Python 3
REPLACER_WINDOWS = REPLACER_WINDOWS.decode('utf-8') REPLACER_WINDOWS = _REPLACER_WINDOWS.decode('utf-8')
REJECTLIST_DIRS = frozenset(('.git', 'test', '.github', '.idea')) REJECTLIST_DIRS = frozenset(('.git', 'test', '.github', '.idea'))
INDENT_REGEX = re.compile(r'([\t]*)') INDENT_REGEX = re.compile(r'([\t]*)')
@ -311,8 +311,8 @@ class ModuleValidator(Validator):
self.analyze_arg_spec = analyze_arg_spec and plugin_type == 'module' self.analyze_arg_spec = analyze_arg_spec and plugin_type == 'module'
self._Version = LooseVersion self._Version: type[LooseVersion | SemanticVersion] = LooseVersion
self._StrictVersion = StrictVersion self._StrictVersion: type[StrictVersion | SemanticVersion] = StrictVersion
self.collection = collection self.collection = collection
self.collection_name = 'ansible.builtin' self.collection_name = 'ansible.builtin'

@ -12,7 +12,7 @@ import yaml
from yaml.resolver import Resolver from yaml.resolver import Resolver
from yaml.constructor import SafeConstructor from yaml.constructor import SafeConstructor
from yaml.error import MarkedYAMLError from yaml.error import MarkedYAMLError
from yaml.cyaml import CParser from yaml.cyaml import CParser # type: ignore[attr-defined]
from yamllint import linter from yamllint import linter
from yamllint.config import YamlLintConfig from yamllint.config import YamlLintConfig
@ -45,17 +45,17 @@ class TestConstructor(SafeConstructor):
TestConstructor.add_constructor( TestConstructor.add_constructor(
'!unsafe', '!unsafe',
TestConstructor.construct_yaml_unsafe) TestConstructor.construct_yaml_unsafe) # type: ignore[type-var]
TestConstructor.add_constructor( TestConstructor.add_constructor(
'!vault', '!vault',
TestConstructor.construct_yaml_str) TestConstructor.construct_yaml_str) # type: ignore[type-var]
TestConstructor.add_constructor( TestConstructor.add_constructor(
'!vault-encrypted', '!vault-encrypted',
TestConstructor.construct_yaml_str) TestConstructor.construct_yaml_str) # type: ignore[type-var]
class TestLoader(CParser, TestConstructor, Resolver): class TestLoader(CParser, TestConstructor, Resolver):
@ -135,11 +135,11 @@ class YamlChecker:
self.messages += [self.result_to_message(r, path, lineno - 1, key) for r in messages] self.messages += [self.result_to_message(r, path, lineno - 1, key) for r in messages]
def check_parsable(self, path, contents, lineno=1, allow_multiple=False, prefix=""): # type: (str, str, int, bool) -> None def check_parsable(self, path: str, contents: str, lineno: int = 1, allow_multiple: bool = False, prefix: str = "") -> None:
"""Check the given contents to verify they can be parsed as YAML.""" """Check the given contents to verify they can be parsed as YAML."""
prefix = f"{prefix}: " if prefix else "" prefix = f"{prefix}: " if prefix else ""
try: try:
documents = len(list(yaml.load_all(contents, Loader=TestLoader))) documents = len(list(yaml.load_all(contents, Loader=TestLoader))) # type: ignore[arg-type]
if documents > 1 and not allow_multiple: if documents > 1 and not allow_multiple:
self.messages += [{'code': 'multiple-yaml-documents', self.messages += [{'code': 'multiple-yaml-documents',
'message': f'{prefix}expected a single document in the stream', 'message': f'{prefix}expected a single document in the stream',

@ -10,7 +10,7 @@ from yaml import load
try: try:
from yaml import CSafeLoader as SafeLoader from yaml import CSafeLoader as SafeLoader
except ImportError: except ImportError:
from yaml import SafeLoader from yaml import SafeLoader # type: ignore[assignment]
# unique ISO date marker matching the one present in importer.py # unique ISO date marker matching the one present in importer.py
ISO_DATE_MARKER = 'isodate:f23983df-f3df-453c-9904-bcd08af468cc:' ISO_DATE_MARKER = 'isodate:f23983df-f3df-453c-9904-bcd08af468cc:'

@ -63,6 +63,8 @@ def run_parent(item, pid, result_path): # type: (Item, int, str) -> list[TestRe
"""Wait for the child process to exit and return the test reports. Called in the parent process.""" """Wait for the child process to exit and return the test reports. Called in the parent process."""
exit_code = waitstatus_to_exitcode(os.waitpid(pid, 0)[1]) exit_code = waitstatus_to_exitcode(os.waitpid(pid, 0)[1])
reports: list[TestReport]
if exit_code: if exit_code:
reason = "Test CRASHED with exit code {}.".format(exit_code) reason = "Test CRASHED with exit code {}.".format(exit_code)
report = TestReport(item.nodeid, item.location, {x: 1 for x in item.keywords}, "failed", reason, "call", user_properties=item.user_properties) report = TestReport(item.nodeid, item.location, {x: 1 for x in item.keywords}, "failed", reason, "call", user_properties=item.user_properties)
@ -73,8 +75,10 @@ def run_parent(item, pid, result_path): # type: (Item, int, str) -> list[TestRe
reports = [report] reports = [report]
else: else:
captured_warnings: list[warnings.WarningMessage]
with open(result_path, "rb") as result_file: with open(result_path, "rb") as result_file:
reports, captured_warnings = pickle.load(result_file) # type: list[TestReport], list[warnings.WarningMessage] reports, captured_warnings = pickle.load(result_file)
for warning in captured_warnings: for warning in captured_warnings:
warnings.warn_explicit(warning.message, warning.category, warning.filename, warning.lineno) warnings.warn_explicit(warning.message, warning.category, warning.filename, warning.lineno)

@ -69,7 +69,7 @@ def enable_assertion_rewriting_hook(): # type: () -> None
# noinspection PyProtectedMember # noinspection PyProtectedMember
from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionPkgLoaderBase from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionPkgLoaderBase
_AnsibleCollectionPkgLoaderBase.exec_module = exec_module _AnsibleCollectionPkgLoaderBase.exec_module = exec_module # type: ignore[method-assign]
def pytest_configure(): def pytest_configure():

@ -62,13 +62,15 @@ def main(): # type: () -> None
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
def bootstrap(pip, options): # type: (str, t.Dict[str, t.Any]) -> None def bootstrap(pip: str, options: dict[str, t.Any]) -> None:
"""Bootstrap pip and related packages in an empty virtual environment.""" """Bootstrap pip and related packages in an empty virtual environment."""
pip_version = options['pip_version'] pip_version = options['pip_version']
packages = options['packages'] packages = options['packages']
setuptools = options['setuptools'] setuptools = options['setuptools']
wheel = options['wheel'] wheel = options['wheel']
del options
url = 'https://ci-files.testing.ansible.com/ansible-test/get-pip-%s.py' % pip_version url = 'https://ci-files.testing.ansible.com/ansible-test/get-pip-%s.py' % pip_version
cache_path = os.path.expanduser('~/.ansible/test/cache/get_pip_%s.py' % pip_version.replace(".", "_")) cache_path = os.path.expanduser('~/.ansible/test/cache/get_pip_%s.py' % pip_version.replace(".", "_"))
temp_path = cache_path + '.download' temp_path = cache_path + '.download'
@ -100,31 +102,33 @@ https://github.com/ansible/ansible/issues/77304
env = common_pip_environment() env = common_pip_environment()
env.update(GET_PIP=cache_path) env.update(GET_PIP=cache_path)
options = common_pip_options() pip_options = common_pip_options()
options.extend(packages) pip_options.extend(packages)
if not setuptools: if not setuptools:
options.append('--no-setuptools') pip_options.append('--no-setuptools')
if not wheel: if not wheel:
options.append('--no-wheel') pip_options.append('--no-wheel')
command = [sys.executable, pip] + options command = [sys.executable, pip] + pip_options
execute_command(command, env=env) execute_command(command, env=env)
def install(pip, options): # type: (str, t.Dict[str, t.Any]) -> None def install(pip: str, options: dict[str, t.Any]) -> None:
"""Perform a pip install.""" """Perform a pip install."""
requirements = options['requirements'] requirements = options['requirements']
constraints = options['constraints'] constraints = options['constraints']
packages = options['packages'] packages = options['packages']
del options
tempdir = tempfile.mkdtemp(prefix='ansible-test-', suffix='-requirements') tempdir = tempfile.mkdtemp(prefix='ansible-test-', suffix='-requirements')
try: try:
options = common_pip_options() pip_options = common_pip_options()
options.extend(packages) pip_options.extend(packages)
for path, content in requirements: for path, content in requirements:
if path.split(os.sep)[0] in ('test', 'requirements'): if path.split(os.sep)[0] in ('test', 'requirements'):
@ -136,13 +140,13 @@ def install(pip, options): # type: (str, t.Dict[str, t.Any]) -> None
pre_build.execute(pip) pre_build.execute(pip)
write_text_file(os.path.join(tempdir, path), content, True) write_text_file(os.path.join(tempdir, path), content, True)
options.extend(['-r', path]) pip_options.extend(['-r', path])
for path, content in constraints: for path, content in constraints:
write_text_file(os.path.join(tempdir, path), content, True) write_text_file(os.path.join(tempdir, path), content, True)
options.extend(['-c', path]) pip_options.extend(['-c', path])
command = [sys.executable, pip, 'install'] + options command = [sys.executable, pip, 'install'] + pip_options
env = common_pip_environment() env = common_pip_environment()
@ -163,8 +167,8 @@ class PreBuild:
tempdir = tempfile.mkdtemp(prefix='ansible-test-', suffix='-pre-build') tempdir = tempfile.mkdtemp(prefix='ansible-test-', suffix='-pre-build')
try: try:
options = common_pip_options() pip_options = common_pip_options()
options.append(self.requirement) pip_options.append(self.requirement)
constraints = '\n'.join(self.constraints) + '\n' constraints = '\n'.join(self.constraints) + '\n'
constraints_path = os.path.join(tempdir, 'constraints.txt') constraints_path = os.path.join(tempdir, 'constraints.txt')
@ -174,7 +178,7 @@ class PreBuild:
env = common_pip_environment() env = common_pip_environment()
env.update(PIP_CONSTRAINT=constraints_path) env.update(PIP_CONSTRAINT=constraints_path)
command = [sys.executable, pip, 'wheel'] + options command = [sys.executable, pip, 'wheel'] + pip_options
execute_command(command, env=env, cwd=tempdir) execute_command(command, env=env, cwd=tempdir)
finally: finally:
@ -206,15 +210,17 @@ def parse_pre_build_instructions(requirements): # type: (str) -> list[PreBuild]
return instructions return instructions
def uninstall(pip, options): # type: (str, t.Dict[str, t.Any]) -> None def uninstall(pip: str, options: dict[str, t.Any]) -> None:
"""Perform a pip uninstall.""" """Perform a pip uninstall."""
packages = options['packages'] packages = options['packages']
ignore_errors = options['ignore_errors'] ignore_errors = options['ignore_errors']
options = common_pip_options() del options
options.extend(packages)
pip_options = common_pip_options()
pip_options.extend(packages)
command = [sys.executable, pip, 'uninstall', '-y'] + options command = [sys.executable, pip, 'uninstall', '-y'] + pip_options
env = common_pip_environment() env = common_pip_environment()
@ -226,13 +232,13 @@ def uninstall(pip, options): # type: (str, t.Dict[str, t.Any]) -> None
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
def version(pip, options): # type: (str, t.Dict[str, t.Any]) -> None def version(pip: str, options: dict[str, t.Any]) -> None:
"""Report the pip version.""" """Report the pip version."""
del options del options
options = common_pip_options() pip_options = common_pip_options()
command = [sys.executable, pip, '-V'] + options command = [sys.executable, pip, '-V'] + pip_options
env = common_pip_environment() env = common_pip_environment()
@ -264,11 +270,11 @@ def common_pip_options(): # type: () -> t.List[str]
def devnull(): # type: () -> t.IO[bytes] def devnull(): # type: () -> t.IO[bytes]
"""Return a file object that references devnull.""" """Return a file object that references devnull."""
try: try:
return devnull.file return devnull.file # type: ignore[attr-defined]
except AttributeError: except AttributeError:
devnull.file = open(os.devnull, 'w+b') # pylint: disable=consider-using-with devnull.file = open(os.devnull, 'w+b') # type: ignore[attr-defined] # pylint: disable=consider-using-with
return devnull.file return devnull.file # type: ignore[attr-defined]
def download_file(url, path): # type: (str, str) -> None def download_file(url, path): # type: (str, str) -> None

@ -5,7 +5,7 @@ import json
try: try:
# virtualenv <20 # virtualenv <20
from sys import real_prefix from sys import real_prefix # type: ignore[attr-defined]
except ImportError: except ImportError:
real_prefix = None real_prefix = None

@ -11,7 +11,7 @@ except ImportError:
try: try:
from yaml import CLoader from yaml import CLoader
except ImportError: except ImportError:
CLoader = None CLoader = None # type: ignore[misc]
print(json.dumps(dict( print(json.dumps(dict(
yaml=bool(yaml), yaml=bool(yaml),

@ -1,10 +1,10 @@
{ {
"prefixes": [ "prefixes": [
"lib/ansible/", "lib/ansible/",
"test/lib/ansible_test/_internal/",
"packaging/", "packaging/",
"test/units", "test/lib/ansible_test/",
"test/lib/ansible_test/_util/target/sanity/import/" "test/sanity/",
"test/units/"
], ],
"extensions": [ "extensions": [
".py" ".py"

@ -31,13 +31,16 @@ def main() -> None:
remote_only_python_versions = os.environ['ANSIBLE_TEST_REMOTE_ONLY_PYTHON_VERSIONS'].split(',') remote_only_python_versions = os.environ['ANSIBLE_TEST_REMOTE_ONLY_PYTHON_VERSIONS'].split(',')
contexts = ( contexts = (
MyPyContext('ansible-test', ['test/lib/ansible_test/_util/target/sanity/import/'], controller_python_versions), MyPyContext('ansible-test', ['test/lib/ansible_test/'], controller_python_versions),
MyPyContext('ansible-test', ['test/lib/ansible_test/_internal/'], controller_python_versions), MyPyContext('ansible-test', ['test/lib/ansible_test/_util/target/'], remote_only_python_versions),
MyPyContext('ansible-core', ['lib/ansible/'], controller_python_versions), MyPyContext('ansible-core', ['lib/ansible/'], controller_python_versions),
MyPyContext('modules', ['lib/ansible/modules/', 'lib/ansible/module_utils/'], remote_only_python_versions), MyPyContext('ansible-core', ['lib/ansible/modules/', 'lib/ansible/module_utils/'], remote_only_python_versions),
MyPyContext('packaging', ['packaging/'], controller_python_versions),
MyPyContext('modules', ['test/units/modules/', 'test/units/module_utils/'], remote_only_python_versions),
MyPyContext('ansible-core', ['test/units/'], controller_python_versions), MyPyContext('ansible-core', ['test/units/'], controller_python_versions),
MyPyContext('ansible-core', ['test/units/modules/', 'test/units/module_utils/'], remote_only_python_versions),
MyPyContext('packaging', ['packaging/'], controller_python_versions),
) )
unfiltered_messages: list[SanityMessage] = [] unfiltered_messages: list[SanityMessage] = []

@ -26,3 +26,45 @@ ignore_missing_imports = True
[mypy-StringIO] [mypy-StringIO]
ignore_missing_imports = True ignore_missing_imports = True
[mypy-voluptuous]
ignore_missing_imports = True
[mypy-voluptuous.humanize]
ignore_missing_imports = True
[mypy-astroid]
ignore_missing_imports = True
[mypy-pylint.interfaces]
ignore_missing_imports = True
[mypy-pylint.checkers]
ignore_missing_imports = True
[mypy-pylint.checkers.utils]
ignore_missing_imports = True
[mypy-pylint.lint]
ignore_missing_imports = True
[mypy-antsibull_docs_parser]
ignore_missing_imports = True
[mypy-antsibull_docs_parser.parser]
ignore_missing_imports = True
[mypy-yamllint]
ignore_missing_imports = True
[mypy-yamllint.config]
ignore_missing_imports = True
[mypy-py]
ignore_missing_imports = True
[mypy-py._path]
ignore_missing_imports = True
[mypy-py._path.local]
ignore_missing_imports = True

@ -1,92 +0,0 @@
# IMPORTANT
# Set "ignore_missing_imports" per package below, rather than globally.
# That will help identify missing type stubs that should be added to the sanity test environment.
[mypy]
[mypy-ansible.module_utils.six.moves.*]
ignore_missing_imports = True
[mypy-pexpect.*]
ignore_missing_imports = True
[mypy-md5.*]
ignore_missing_imports = True
[mypy-rpmUtils.*]
ignore_missing_imports = True
[mypy-rpm.*]
ignore_missing_imports = True
[mypy-psutil.*]
ignore_missing_imports = True
[mypy-dnf.*]
ignore_missing_imports = True
[mypy-apt.*]
ignore_missing_imports = True
[mypy-apt_pkg.*]
ignore_missing_imports = True
[mypy-gssapi.*]
ignore_missing_imports = True
[mypy-_ssl.*]
ignore_missing_imports = True
[mypy-urllib_gssapi.*]
ignore_missing_imports = True
[mypy-systemd.*]
ignore_missing_imports = True
[mypy-sha.*]
ignore_missing_imports = True
[mypy-distro.*]
ignore_missing_imports = True
[mypy-selinux.*]
ignore_missing_imports = True
[mypy-urllib2.*]
ignore_missing_imports = True
[mypy-httplib.*]
ignore_missing_imports = True
[mypy-compiler.*]
ignore_missing_imports = True
[mypy-aptsources.*]
ignore_missing_imports = True
[mypy-urllib3.*]
ignore_missing_imports = True
[mypy-requests.*]
ignore_missing_imports = True
[mypy-pkg_resources.*]
ignore_missing_imports = True
[mypy-urllib.*]
ignore_missing_imports = True
[mypy-email.*]
ignore_missing_imports = True
[mypy-selectors.*]
ignore_missing_imports = True
[mypy-importlib.*]
ignore_missing_imports = True
[mypy-collections.*]
ignore_missing_imports = True
[mypy-http.*]
ignore_missing_imports = True
Loading…
Cancel
Save