mirror of
https://github.com/zebrajr/ansible.git
synced 2026-01-15 12:15:19 +00:00
ansible-test - Improve deprecated checking type inference (#85159)
* ansible-test - Improve deprecated checking type inference Also disabled the ``bad-super-call`` pylint rule due to false positives. * Add type comment support * Try without using register_transform
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
bugfixes:
|
||||
- ansible-test - Improve type inference for pylint deprecated checks to accommodate some type annotations.
|
||||
- ansible-test - Disabled the ``bad-super-call`` pylint rule due to false positives.
|
||||
@@ -19,6 +19,7 @@ import ansible.module_utils.common.warnings
|
||||
|
||||
from ansible.module_utils import datatag
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.basic import AnsibleModule as AliasedAnsibleModule
|
||||
from ansible.module_utils.basic import deprecate
|
||||
from ansible.module_utils.common import warnings
|
||||
from ansible.module_utils.common.warnings import deprecate as basic_deprecate
|
||||
@@ -39,7 +40,6 @@ foreign_global_display = x_display._display
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
@@ -90,3 +90,43 @@ def do_stuff() -> None:
|
||||
a_version = 'version not checked'
|
||||
a_collection_name = 'mismatched'
|
||||
_display.deprecated(msg=a_msg, date=a_date, version=a_version, collection_name=a_collection_name)
|
||||
|
||||
wrapper = MyWrapper(AnsibleModule({}))
|
||||
wrapper.module.deprecate('', version='2.0.0', collection_name='ns.col')
|
||||
|
||||
wrapper = MyAliasedWrapper(AnsibleModule({}))
|
||||
wrapper.module.deprecate('', version='2.0.0', collection_name='ns.col')
|
||||
|
||||
wrapper = MyOtherWrapper(AnsibleModule({}))
|
||||
wrapper.module.deprecate('', version='2.0.0', collection_name='ns.col')
|
||||
|
||||
wrapper = MyOtherAliasedWrapper(AnsibleModule({}))
|
||||
wrapper.module.deprecate('', version='2.0.0', collection_name='ns.col')
|
||||
|
||||
wrapper = MyTypeCommentWrapper(AnsibleModule({}))
|
||||
wrapper.module.deprecate('', version='2.0.0', collection_name='ns.col')
|
||||
|
||||
|
||||
class MyWrapper:
|
||||
def __init__(self, thing) -> None:
|
||||
self.module: AnsibleModule = thing
|
||||
|
||||
|
||||
class MyAliasedWrapper:
|
||||
def __init__(self, thing) -> None:
|
||||
self.module: AliasedAnsibleModule = thing
|
||||
|
||||
|
||||
class MyOtherWrapper:
|
||||
def __init__(self, thing: AnsibleModule) -> None:
|
||||
self.module = thing
|
||||
|
||||
|
||||
class MyOtherAliasedWrapper:
|
||||
def __init__(self, thing: AliasedAnsibleModule) -> None:
|
||||
self.module = thing
|
||||
|
||||
|
||||
class MyTypeCommentWrapper:
|
||||
def __init__(self, thing) -> None:
|
||||
self.module = thing # type: AnsibleModule
|
||||
|
||||
@@ -25,6 +25,11 @@ plugins/lookup/deprecated.py:81:4: collection-deprecated-version: Deprecated ver
|
||||
plugins/lookup/deprecated.py:82:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.utils.display.Display.deprecated'
|
||||
plugins/lookup/deprecated.py:83:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.utils.display.Display.deprecated'
|
||||
plugins/lookup/deprecated.py:84:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.utils.display.Display.deprecated'
|
||||
plugins/lookup/deprecated.py:95:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.module_utils.basic.AnsibleModule.deprecate'
|
||||
plugins/lookup/deprecated.py:98:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.module_utils.basic.AnsibleModule.deprecate'
|
||||
plugins/lookup/deprecated.py:101:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.module_utils.basic.AnsibleModule.deprecate'
|
||||
plugins/lookup/deprecated.py:104:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.module_utils.basic.AnsibleModule.deprecate'
|
||||
plugins/lookup/deprecated.py:107:4: collection-deprecated-version: Deprecated version '2.0.0' found in call to 'ansible.module_utils.basic.AnsibleModule.deprecate'
|
||||
plugins/module_utils/deprecated_utils.py:21:4: ansible-deprecated-no-version: Found 'ansible.module_utils.common.warnings.deprecate' call without a version or date
|
||||
plugins/module_utils/deprecated_utils.py:23:4: collection-deprecated-version: Deprecated version '1.0.0' found in call to 'ansible.module_utils.common.warnings.deprecate'
|
||||
plugins/module_utils/deprecated_utils.py:24:4: collection-invalid-deprecated-version: Invalid deprecated version 'not-a-version' found in call to 'ansible.module_utils.common.warnings.deprecate'
|
||||
|
||||
@@ -6,6 +6,7 @@ load-plugins=
|
||||
pylint.extensions.docstyle,
|
||||
|
||||
disable=
|
||||
bad-super-call, # flakey test, can report false positives due to inference issue when using deprecate_calls plugin
|
||||
docstring-first-line-empty,
|
||||
consider-using-f-string, # Python 2.x support still required
|
||||
cyclic-import, # consistent results require running with --jobs 1 and testing all files
|
||||
|
||||
@@ -6,6 +6,7 @@ load-plugins=
|
||||
pylint.extensions.docstyle,
|
||||
|
||||
disable=
|
||||
bad-super-call, # flakey test, can report false positives due to inference issue when using deprecate_calls plugin
|
||||
docstring-first-line-empty,
|
||||
consider-using-f-string, # many occurrences
|
||||
cyclic-import, # consistent results require running with --jobs 1 and testing all files
|
||||
|
||||
@@ -6,6 +6,7 @@ load-plugins=
|
||||
pylint.extensions.docstyle,
|
||||
|
||||
disable=
|
||||
bad-super-call, # flakey test, can report false positives due to inference issue when using deprecate_calls plugin
|
||||
docstring-first-line-empty,
|
||||
consider-using-f-string, # many occurrences
|
||||
cyclic-import, # consistent results require running with --jobs 1 and testing all files
|
||||
|
||||
@@ -11,6 +11,7 @@ disable=
|
||||
attribute-defined-outside-init,
|
||||
bad-indentation,
|
||||
bad-mcs-classmethod-argument,
|
||||
bad-super-call, # flakey test, can report false positives due to inference issue when using deprecate_calls plugin
|
||||
broad-exception-caught,
|
||||
broad-exception-raised,
|
||||
c-extension-no-member,
|
||||
|
||||
@@ -16,6 +16,7 @@ disable=
|
||||
attribute-defined-outside-init,
|
||||
bad-indentation,
|
||||
bad-mcs-classmethod-argument,
|
||||
bad-super-call, # flakey test, can report false positives due to inference issue when using deprecate_calls plugin
|
||||
broad-exception-caught,
|
||||
broad-exception-raised,
|
||||
c-extension-no-member,
|
||||
|
||||
@@ -263,6 +263,20 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
break
|
||||
|
||||
for name in reversed(names):
|
||||
if isinstance(inferred, astroid.Instance):
|
||||
try:
|
||||
attr = next(iter(inferred.getattr(name)), None)
|
||||
except astroid.AttributeInferenceError:
|
||||
break
|
||||
|
||||
if isinstance(attr, astroid.AssignAttr):
|
||||
inferred = self.get_ansible_module(attr)
|
||||
continue
|
||||
|
||||
if isinstance(attr, astroid.FunctionDef):
|
||||
inferred = attr
|
||||
continue
|
||||
|
||||
if not isinstance(inferred, (astroid.Module, astroid.ClassDef)):
|
||||
inferred = None
|
||||
break
|
||||
@@ -282,25 +296,46 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
def infer_name(self, node: astroid.Name) -> astroid.NodeNG | None:
|
||||
"""Infer the node referenced by the given name, or `None` if it cannot be unambiguously inferred."""
|
||||
scope = node.scope()
|
||||
name = None
|
||||
inferred: astroid.NodeNG | None = None
|
||||
name = node.name
|
||||
|
||||
while scope:
|
||||
try:
|
||||
assignment = scope[node.name]
|
||||
assignment = scope[name]
|
||||
except KeyError:
|
||||
scope = scope.parent.scope() if scope.parent else None
|
||||
continue
|
||||
|
||||
if isinstance(assignment, astroid.AssignName) and isinstance(assignment.parent, astroid.Assign):
|
||||
name = assignment.parent.value
|
||||
inferred = assignment.parent.value
|
||||
elif (
|
||||
isinstance(scope, astroid.FunctionDef)
|
||||
and isinstance(assignment, astroid.AssignName)
|
||||
and isinstance(assignment.parent, astroid.Arguments)
|
||||
and assignment.parent.annotations
|
||||
):
|
||||
idx, _node = assignment.parent.find_argname(name)
|
||||
|
||||
if idx is not None:
|
||||
try:
|
||||
annotation = assignment.parent.annotations[idx]
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(annotation, astroid.Name):
|
||||
name = annotation.name
|
||||
continue
|
||||
elif isinstance(assignment, astroid.ClassDef):
|
||||
inferred = assignment
|
||||
elif isinstance(assignment, astroid.ImportFrom):
|
||||
if module := self.get_module(assignment):
|
||||
name = assignment.real_name(name)
|
||||
scope = module.scope()
|
||||
continue
|
||||
|
||||
break
|
||||
|
||||
return name
|
||||
return inferred
|
||||
|
||||
def get_module(self, node: astroid.ImportFrom) -> astroid.Module | None:
|
||||
"""Import the requested module if possible and cache the result."""
|
||||
@@ -480,7 +515,27 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
|
||||
raise TypeError(type(value))
|
||||
|
||||
def get_ansible_module(self, node: astroid.AssignAttr) -> astroid.Instance | None:
|
||||
"""Infer an AnsibleModule instance node from the given assignment."""
|
||||
if isinstance(node.parent, astroid.Assign) and isinstance(node.parent.type_annotation, astroid.Name):
|
||||
inferred = self.infer_name(node.parent.type_annotation)
|
||||
elif isinstance(node.parent, astroid.Assign) and isinstance(node.parent.parent, astroid.FunctionDef) and isinstance(node.parent.value, astroid.Name):
|
||||
inferred = self.infer_name(node.parent.value)
|
||||
elif isinstance(node.parent, astroid.AnnAssign) and isinstance(node.parent.annotation, astroid.Name):
|
||||
inferred = self.infer_name(node.parent.annotation)
|
||||
else:
|
||||
inferred = None
|
||||
|
||||
if isinstance(inferred, astroid.ClassDef) and inferred.name == 'AnsibleModule':
|
||||
return inferred.instantiate_class()
|
||||
|
||||
return None
|
||||
|
||||
def register(self) -> None:
|
||||
"""Register this plugin."""
|
||||
self.linter.register_checker(self)
|
||||
|
||||
|
||||
def register(linter: pylint.lint.PyLinter) -> None:
|
||||
"""Required method to auto-register this checker."""
|
||||
linter.register_checker(AnsibleDeprecatedChecker(linter))
|
||||
AnsibleDeprecatedChecker(linter).register()
|
||||
|
||||
Reference in New Issue
Block a user