mirror of
https://github.com/ansible/ansible.git
synced 2025-11-30 23:16:08 +07:00
ansible-test - Update sanity test requirements (#85980)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
minor_changes:
|
||||
- ansible-test - Update pinned sanity test requirements, including upgrading to pylint 4.0.0.
|
||||
- ansible-test - Filter out pylint messages for invalid filenames and display a notice when doing so.
|
||||
- ansible-test - Update astroid imports in custom pylint checkers.
|
||||
- ansible-test - Default to Python 3.14 in the ``base`` and ``default`` test containers.
|
||||
@@ -12,4 +12,4 @@ packaging
|
||||
# NOTE: Ref: https://github.com/sarugaku/resolvelib/issues/69
|
||||
# NOTE: When updating the upper bound, also update the latest version used
|
||||
# NOTE: in the ansible-galaxy-collection test suite.
|
||||
resolvelib >= 0.5.3, < 2.0.0 # dependency resolver used by ansible-galaxy
|
||||
resolvelib >= 0.8.0, < 2.0.0 # dependency resolver used by ansible-galaxy
|
||||
|
||||
@@ -14,4 +14,4 @@ import string # pylint: disable=unused-import
|
||||
# 'Call' object has no attribute 'value'
|
||||
result = {None: None}[{}.get('something')]
|
||||
|
||||
foo = {}.keys()
|
||||
foo = {}.keys() # should trigger disallowed-name, but doesn't in pylint 4.0.0, probably due to https://github.com/pylint-dev/pylint/issues/10652
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
plugins/modules/bad.py import
|
||||
plugins/modules/bad.py pylint:ansible-bad-module-import
|
||||
plugins/lookup/bad.py import
|
||||
plugins/plugin_utils/check_pylint.py pylint:disallowed-name
|
||||
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-function
|
||||
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import
|
||||
tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import-from
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
base image=quay.io/ansible/base-test-container:v2.20-1 python=3.13,3.9,3.10,3.11,3.12,3.14
|
||||
default image=quay.io/ansible/default-test-container:v2.20-2 python=3.13,3.9,3.10,3.11,3.12,3.14 context=collection
|
||||
default image=quay.io/ansible/ansible-core-test-container:v2.20-2 python=3.13,3.9,3.10,3.11,3.12,3.14 context=ansible-core
|
||||
base image=quay.io/ansible/base-test-container:v2.20-1 python=3.14,3.9,3.10,3.11,3.12,3.13
|
||||
default image=quay.io/ansible/default-test-container:v2.20-2 python=3.14,3.9,3.10,3.11,3.12,3.13 context=collection
|
||||
default image=quay.io/ansible/ansible-core-test-container:v2.20-2 python=3.14,3.9,3.10,3.11,3.12,3.13 context=ansible-core
|
||||
alpine322 image=quay.io/ansible/alpine-test-container:3.22-v2.20-0 python=3.12 cgroup=none audit=none
|
||||
fedora42 image=quay.io/ansible/fedora-test-container:42-v2.20-0 python=3.13 cgroup=v2-only
|
||||
ubuntu2204 image=quay.io/ansible/ubuntu-test-container:22.04-v2.20-0 python=3.10
|
||||
|
||||
@@ -12,4 +12,4 @@ packaging
|
||||
# NOTE: Ref: https://github.com/sarugaku/resolvelib/issues/69
|
||||
# NOTE: When updating the upper bound, also update the latest version used
|
||||
# NOTE: in the ansible-galaxy-collection test suite.
|
||||
resolvelib >= 0.5.3, < 2.0.0 # dependency resolver used by ansible-galaxy
|
||||
resolvelib >= 0.8.0, < 2.0.0 # dependency resolver used by ansible-galaxy
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# edit "sanity.ansible-doc.in" and generate with: hacking/update-sanity-requirements.py --test ansible-doc
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
MarkupSafe==3.0.3
|
||||
packaging==25.0
|
||||
PyYAML==6.0.2
|
||||
PyYAML==6.0.3
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
antsibull-changelog==0.29.0
|
||||
docutils==0.18.1
|
||||
packaging==25.0
|
||||
PyYAML==6.0.2
|
||||
PyYAML==6.0.3
|
||||
rstcheck==5.0.0
|
||||
semantic-version==2.10.0
|
||||
types-docutils==0.18.3
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# edit "sanity.import.plugin.in" and generate with: hacking/update-sanity-requirements.py --test import.plugin
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
PyYAML==6.0.2
|
||||
MarkupSafe==3.0.3
|
||||
PyYAML==6.0.3
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# edit "sanity.import.in" and generate with: hacking/update-sanity-requirements.py --test import
|
||||
PyYAML==6.0.2
|
||||
PyYAML==6.0.3
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# edit "sanity.integration-aliases.in" and generate with: hacking/update-sanity-requirements.py --test integration-aliases
|
||||
PyYAML==6.0.2
|
||||
PyYAML==6.0.3
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# edit "sanity.pylint.in" and generate with: hacking/update-sanity-requirements.py --test pylint
|
||||
astroid==3.3.11
|
||||
astroid==4.0.1
|
||||
dill==0.4.0
|
||||
isort==6.0.1
|
||||
isort==7.0.0
|
||||
mccabe==0.7.0
|
||||
platformdirs==4.4.0
|
||||
pylint==3.3.8
|
||||
PyYAML==6.0.2
|
||||
platformdirs==4.5.0
|
||||
pylint==4.0.0
|
||||
PyYAML==6.0.3
|
||||
tomlkit==0.13.3
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# edit "sanity.runtime-metadata.in" and generate with: hacking/update-sanity-requirements.py --test runtime-metadata
|
||||
PyYAML==6.0.2
|
||||
PyYAML==6.0.3
|
||||
voluptuous==0.15.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# edit "sanity.validate-modules.in" and generate with: hacking/update-sanity-requirements.py --test validate-modules
|
||||
antsibull-docs-parser==1.0.0
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
PyYAML==6.0.2
|
||||
MarkupSafe==3.0.3
|
||||
PyYAML==6.0.3
|
||||
voluptuous==0.15.2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# edit "sanity.yamllint.in" and generate with: hacking/update-sanity-requirements.py --test yamllint
|
||||
pathspec==0.12.1
|
||||
PyYAML==6.0.2
|
||||
PyYAML==6.0.3
|
||||
yamllint==1.37.1
|
||||
|
||||
@@ -301,4 +301,15 @@ class PylintTest(SanitySingleVersion):
|
||||
else:
|
||||
messages = []
|
||||
|
||||
expected_paths = set(paths)
|
||||
|
||||
unexpected_messages = [message for message in messages if message["path"] not in expected_paths]
|
||||
messages = [message for message in messages if message["path"] in expected_paths]
|
||||
|
||||
for unexpected_message in unexpected_messages:
|
||||
display.info(f"Unexpected message: {json.dumps(unexpected_message)}", verbosity=4)
|
||||
|
||||
if unexpected_messages:
|
||||
display.notice(f"Discarded {len(unexpected_messages)} unexpected messages. Use -vvvv to display.")
|
||||
|
||||
return messages
|
||||
|
||||
@@ -11,9 +11,11 @@ import functools
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
import astroid
|
||||
import astroid.context
|
||||
import astroid.bases
|
||||
import astroid.exceptions
|
||||
import astroid.nodes
|
||||
import astroid.typing
|
||||
import astroid.util
|
||||
|
||||
import pylint.lint
|
||||
import pylint.checkers
|
||||
@@ -42,7 +44,7 @@ class DeprecationCallArgs:
|
||||
|
||||
def all_args_dynamic(self) -> bool:
|
||||
"""True if all args are dynamic or None, otherwise False."""
|
||||
return all(arg is None or isinstance(arg, astroid.NodeNG) for arg in dataclasses.asdict(self).values())
|
||||
return all(arg is None or isinstance(arg, astroid.nodes.NodeNG) for arg in dataclasses.asdict(self).values())
|
||||
|
||||
|
||||
class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
@@ -177,7 +179,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.module_cache: dict[str, astroid.Module] = {}
|
||||
self.module_cache: dict[str, astroid.nodes.Module] = {}
|
||||
|
||||
@functools.cached_property
|
||||
def collection_name(self) -> str | None:
|
||||
@@ -226,7 +228,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
return None
|
||||
|
||||
@pylint.checkers.utils.only_required_for_messages(*(msgs.keys()))
|
||||
def visit_call(self, node: astroid.Call) -> None:
|
||||
def visit_call(self, node: astroid.nodes.Call) -> None:
|
||||
"""Visit a call node."""
|
||||
if inferred := self.infer(node.func):
|
||||
name = self.get_fully_qualified_name(inferred)
|
||||
@@ -234,50 +236,50 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
if args := self.DEPRECATION_FUNCTIONS.get(name):
|
||||
self.check_call(node, name, args)
|
||||
|
||||
def infer(self, node: astroid.NodeNG) -> astroid.NodeNG | None:
|
||||
def infer(self, node: astroid.nodes.NodeNG) -> astroid.nodes.NodeNG | None:
|
||||
"""Return the inferred node from the given node, or `None` if it cannot be unambiguously inferred."""
|
||||
names: list[str] = []
|
||||
target: astroid.NodeNG | None = node
|
||||
target: astroid.nodes.NodeNG | None = node
|
||||
inferred: astroid.typing.InferenceResult | None = None
|
||||
|
||||
while target:
|
||||
if inferred := astroid.util.safe_infer(target):
|
||||
break
|
||||
|
||||
if isinstance(target, astroid.Call):
|
||||
if isinstance(target, astroid.nodes.Call):
|
||||
inferred = self.infer(target.func)
|
||||
break
|
||||
|
||||
if isinstance(target, astroid.FunctionDef):
|
||||
if isinstance(target, astroid.nodes.FunctionDef):
|
||||
inferred = target
|
||||
break
|
||||
|
||||
if isinstance(target, astroid.Name):
|
||||
if isinstance(target, astroid.nodes.Name):
|
||||
target = self.infer_name(target)
|
||||
elif isinstance(target, astroid.AssignName) and isinstance(target.parent, astroid.Assign):
|
||||
elif isinstance(target, astroid.nodes.AssignName) and isinstance(target.parent, astroid.nodes.Assign):
|
||||
target = target.parent.value
|
||||
elif isinstance(target, astroid.Attribute):
|
||||
elif isinstance(target, astroid.nodes.Attribute):
|
||||
names.append(target.attrname)
|
||||
target = target.expr
|
||||
else:
|
||||
break
|
||||
|
||||
for name in reversed(names):
|
||||
if isinstance(inferred, astroid.Instance):
|
||||
if isinstance(inferred, astroid.bases.Instance):
|
||||
try:
|
||||
attr = next(iter(inferred.getattr(name)), None)
|
||||
except astroid.AttributeInferenceError:
|
||||
except astroid.exceptions.AttributeInferenceError:
|
||||
break
|
||||
|
||||
if isinstance(attr, astroid.AssignAttr):
|
||||
if isinstance(attr, astroid.nodes.AssignAttr):
|
||||
inferred = self.get_ansible_module(attr)
|
||||
continue
|
||||
|
||||
if isinstance(attr, astroid.FunctionDef):
|
||||
if isinstance(attr, astroid.nodes.FunctionDef):
|
||||
inferred = attr
|
||||
continue
|
||||
|
||||
if not isinstance(inferred, (astroid.Module, astroid.ClassDef)):
|
||||
if not isinstance(inferred, (astroid.nodes.Module, astroid.nodes.ClassDef)):
|
||||
inferred = None
|
||||
break
|
||||
|
||||
@@ -288,15 +290,15 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
else:
|
||||
inferred = self.infer(inferred)
|
||||
|
||||
if isinstance(inferred, astroid.FunctionDef) and isinstance(inferred.parent, astroid.ClassDef):
|
||||
inferred = astroid.BoundMethod(inferred, inferred.parent)
|
||||
if isinstance(inferred, astroid.nodes.FunctionDef) and isinstance(inferred.parent, astroid.nodes.ClassDef):
|
||||
inferred = astroid.bases.BoundMethod(inferred, inferred.parent)
|
||||
|
||||
return inferred
|
||||
|
||||
def infer_name(self, node: astroid.Name) -> astroid.NodeNG | None:
|
||||
def infer_name(self, node: astroid.nodes.Name) -> astroid.nodes.NodeNG | None:
|
||||
"""Infer the node referenced by the given name, or `None` if it cannot be unambiguously inferred."""
|
||||
scope = node.scope()
|
||||
inferred: astroid.NodeNG | None = None
|
||||
inferred: astroid.nodes.NodeNG | None = None
|
||||
name = node.name
|
||||
|
||||
while scope:
|
||||
@@ -306,12 +308,12 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
scope = scope.parent.scope() if scope.parent else None
|
||||
continue
|
||||
|
||||
if isinstance(assignment, astroid.AssignName) and isinstance(assignment.parent, astroid.Assign):
|
||||
if isinstance(assignment, astroid.nodes.AssignName) and isinstance(assignment.parent, astroid.nodes.Assign):
|
||||
inferred = assignment.parent.value
|
||||
elif (
|
||||
isinstance(scope, astroid.FunctionDef)
|
||||
and isinstance(assignment, astroid.AssignName)
|
||||
and isinstance(assignment.parent, astroid.Arguments)
|
||||
isinstance(scope, astroid.nodes.FunctionDef)
|
||||
and isinstance(assignment, astroid.nodes.AssignName)
|
||||
and isinstance(assignment.parent, astroid.nodes.Arguments)
|
||||
and assignment.parent.annotations
|
||||
):
|
||||
idx, _node = assignment.parent.find_argname(name)
|
||||
@@ -322,12 +324,12 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(annotation, astroid.Name):
|
||||
if isinstance(annotation, astroid.nodes.Name):
|
||||
name = annotation.name
|
||||
continue
|
||||
elif isinstance(assignment, astroid.ClassDef):
|
||||
elif isinstance(assignment, astroid.nodes.ClassDef):
|
||||
inferred = assignment
|
||||
elif isinstance(assignment, astroid.ImportFrom):
|
||||
elif isinstance(assignment, astroid.nodes.ImportFrom):
|
||||
if module := self.get_module(assignment):
|
||||
name = assignment.real_name(name)
|
||||
scope = module.scope()
|
||||
@@ -337,7 +339,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
|
||||
return inferred
|
||||
|
||||
def get_module(self, node: astroid.ImportFrom) -> astroid.Module | None:
|
||||
def get_module(self, node: astroid.nodes.ImportFrom) -> astroid.nodes.Module | None:
|
||||
"""Import the requested module if possible and cache the result."""
|
||||
module_name = pylint.checkers.utils.get_import_name(node, node.modname)
|
||||
|
||||
@@ -357,21 +359,21 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
return module
|
||||
|
||||
@staticmethod
|
||||
def get_fully_qualified_name(node: astroid.NodeNG) -> str | None:
|
||||
def get_fully_qualified_name(node: astroid.nodes.NodeNG) -> str | None:
|
||||
"""Return the fully qualified name of the given inferred node."""
|
||||
parent = node.parent
|
||||
parts: tuple[str, ...] | None
|
||||
|
||||
if isinstance(node, astroid.FunctionDef) and isinstance(parent, astroid.Module):
|
||||
if isinstance(node, astroid.nodes.FunctionDef) and isinstance(parent, astroid.nodes.Module):
|
||||
parts = (parent.name, node.name)
|
||||
elif isinstance(node, astroid.BoundMethod) and isinstance(parent, astroid.ClassDef) and isinstance(parent.parent, astroid.Module):
|
||||
elif isinstance(node, astroid.bases.BoundMethod) and isinstance(parent, astroid.nodes.ClassDef) and isinstance(parent.parent, astroid.nodes.Module):
|
||||
parts = (parent.parent.name, parent.name, node.name)
|
||||
else:
|
||||
parts = None
|
||||
|
||||
return '.'.join(parts) if parts else None
|
||||
|
||||
def check_call(self, node: astroid.Call, name: str, args: tuple[str, ...]) -> None:
|
||||
def check_call(self, node: astroid.nodes.Call, name: str, args: tuple[str, ...]) -> None:
|
||||
"""Check the given deprecation call node for valid arguments."""
|
||||
call_args = self.get_deprecation_call_args(node, args)
|
||||
|
||||
@@ -400,7 +402,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
self.check_version(node, name, call_args)
|
||||
|
||||
@staticmethod
|
||||
def get_deprecation_call_args(node: astroid.Call, args: tuple[str, ...]) -> DeprecationCallArgs:
|
||||
def get_deprecation_call_args(node: astroid.nodes.Call, args: tuple[str, ...]) -> DeprecationCallArgs:
|
||||
"""Get the deprecation call arguments from the given node."""
|
||||
fields: dict[str, object] = {}
|
||||
|
||||
@@ -413,12 +415,12 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
fields[keyword.arg] = keyword.value
|
||||
|
||||
for key, value in fields.items():
|
||||
if isinstance(value, astroid.Const):
|
||||
if isinstance(value, astroid.nodes.Const):
|
||||
fields[key] = value.value
|
||||
|
||||
return DeprecationCallArgs(**fields)
|
||||
|
||||
def check_collection_name(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
def check_collection_name(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
"""Check the collection name provided to the given call node."""
|
||||
deprecator_requirement = self.is_deprecator_required()
|
||||
|
||||
@@ -459,14 +461,14 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
if args.collection_name and args.collection_name != expected_collection_name:
|
||||
self.add_message('wrong-collection-deprecated', node=node, args=(args.collection_name, name))
|
||||
|
||||
def check_version(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
def check_version(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
"""Check the version provided to the given call node."""
|
||||
if self.collection_name:
|
||||
self.check_collection_version(node, name, args)
|
||||
else:
|
||||
self.check_core_version(node, name, args)
|
||||
|
||||
def check_core_version(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
def check_core_version(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
"""Check the core version provided to the given call node."""
|
||||
try:
|
||||
if not isinstance(args.version, str) or not args.version:
|
||||
@@ -480,7 +482,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
if self.ANSIBLE_VERSION >= strict_version:
|
||||
self.add_message('ansible-deprecated-version', node=node, args=(args.version, name))
|
||||
|
||||
def check_collection_version(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
def check_collection_version(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
"""Check the collection version provided to the given call node."""
|
||||
try:
|
||||
if not isinstance(args.version, str) or not args.version:
|
||||
@@ -497,7 +499,7 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
if semantic_version.major != 0 and (semantic_version.minor != 0 or semantic_version.patch != 0):
|
||||
self.add_message('removal-version-must-be-major', node=node, args=(args.version,))
|
||||
|
||||
def check_date(self, node: astroid.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
def check_date(self, node: astroid.nodes.Call, name: str, args: DeprecationCallArgs) -> None:
|
||||
"""Check the date provided to the given call node."""
|
||||
try:
|
||||
date_parsed = self.parse_isodate(args.date)
|
||||
@@ -515,18 +517,19 @@ class AnsibleDeprecatedChecker(pylint.checkers.BaseChecker):
|
||||
|
||||
raise TypeError(type(value))
|
||||
|
||||
def get_ansible_module(self, node: astroid.AssignAttr) -> astroid.Instance | None:
|
||||
def get_ansible_module(self, node: astroid.nodes.AssignAttr) -> astroid.bases.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):
|
||||
if isinstance(node.parent, astroid.nodes.Assign) and isinstance(node.parent.type_annotation, astroid.nodes.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):
|
||||
elif (isinstance(node.parent, astroid.nodes.Assign) and isinstance(node.parent.parent, astroid.nodes.FunctionDef) and
|
||||
isinstance(node.parent.value, astroid.nodes.Name)):
|
||||
inferred = self.infer_name(node.parent.value)
|
||||
elif isinstance(node.parent, astroid.AnnAssign) and isinstance(node.parent.annotation, astroid.Name):
|
||||
elif isinstance(node.parent, astroid.nodes.AnnAssign) and isinstance(node.parent.annotation, astroid.nodes.Name):
|
||||
inferred = self.infer_name(node.parent.annotation)
|
||||
else:
|
||||
inferred = None
|
||||
|
||||
if isinstance(inferred, astroid.ClassDef) and inferred.name == 'AnsibleModule':
|
||||
if isinstance(inferred, astroid.nodes.ClassDef) and inferred.name == 'AnsibleModule':
|
||||
return inferred.instantiate_class()
|
||||
|
||||
return None
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import astroid
|
||||
import astroid.bases
|
||||
import astroid.exceptions
|
||||
import astroid.nodes
|
||||
|
||||
try:
|
||||
from pylint.checkers.utils import check_messages
|
||||
@@ -39,22 +41,22 @@ class AnsibleStringFormatChecker(BaseChecker):
|
||||
def visit_call(self, node):
|
||||
"""Visit a call node."""
|
||||
func = utils.safe_infer(node.func)
|
||||
if (isinstance(func, astroid.BoundMethod)
|
||||
and isinstance(func.bound, astroid.Instance)
|
||||
if (isinstance(func, astroid.bases.BoundMethod)
|
||||
and isinstance(func.bound, astroid.bases.Instance)
|
||||
and func.bound.name in ('str', 'unicode', 'bytes')):
|
||||
if func.name == 'format':
|
||||
self._check_new_format(node, func)
|
||||
|
||||
def _check_new_format(self, node, func):
|
||||
""" Check the new string formatting """
|
||||
if (isinstance(node.func, astroid.Attribute)
|
||||
and not isinstance(node.func.expr, astroid.Const)):
|
||||
if (isinstance(node.func, astroid.nodes.Attribute)
|
||||
and not isinstance(node.func.expr, astroid.nodes.Const)):
|
||||
return
|
||||
try:
|
||||
strnode = next(func.bound.infer())
|
||||
except astroid.InferenceError:
|
||||
except astroid.exceptions.InferenceError:
|
||||
return
|
||||
if not isinstance(strnode, astroid.Const):
|
||||
if not isinstance(strnode, astroid.nodes.Const):
|
||||
return
|
||||
|
||||
if isinstance(strnode.value, bytes):
|
||||
|
||||
@@ -6,7 +6,8 @@ import functools
|
||||
import os
|
||||
import typing as t
|
||||
|
||||
import astroid
|
||||
import astroid.exceptions
|
||||
import astroid.nodes
|
||||
|
||||
from pylint.checkers import BaseChecker
|
||||
|
||||
@@ -146,21 +147,21 @@ class AnsibleUnwantedChecker(BaseChecker):
|
||||
"""True if ansible-core is being tested."""
|
||||
return not self.linter.config.collection_name
|
||||
|
||||
def visit_import(self, node): # type: (astroid.node_classes.Import) -> None
|
||||
def visit_import(self, node: astroid.nodes.Import) -> None:
|
||||
"""Visit an import node."""
|
||||
for name in node.names:
|
||||
self._check_import(node, name[0])
|
||||
|
||||
def visit_importfrom(self, node): # type: (astroid.node_classes.ImportFrom) -> None
|
||||
def visit_importfrom(self, node: astroid.nodes.ImportFrom) -> None:
|
||||
"""Visit an import from node."""
|
||||
self._check_importfrom(node, node.modname, node.names)
|
||||
|
||||
def visit_attribute(self, node): # type: (astroid.node_classes.Attribute) -> None
|
||||
def visit_attribute(self, node: astroid.nodes.Attribute) -> None:
|
||||
"""Visit an attribute node."""
|
||||
last_child = node.last_child()
|
||||
|
||||
# this is faster than using type inference and will catch the most common cases
|
||||
if not isinstance(last_child, astroid.node_classes.Name):
|
||||
if not isinstance(last_child, astroid.nodes.Name):
|
||||
return
|
||||
|
||||
module = last_child.name
|
||||
@@ -171,13 +172,13 @@ class AnsibleUnwantedChecker(BaseChecker):
|
||||
if entry.applies_to(self.linter.current_file, node.attrname):
|
||||
self.add_message(self.BAD_IMPORT_FROM, args=(node.attrname, entry.alternative, module), node=node)
|
||||
|
||||
def visit_call(self, node): # type: (astroid.node_classes.Call) -> None
|
||||
def visit_call(self, node: astroid.nodes.Call) -> None:
|
||||
"""Visit a call node."""
|
||||
try:
|
||||
for i in node.func.inferred():
|
||||
func = None
|
||||
|
||||
if isinstance(i, astroid.scoped_nodes.FunctionDef) and isinstance(i.parent, astroid.scoped_nodes.Module):
|
||||
if isinstance(i, astroid.nodes.FunctionDef) and isinstance(i.parent, astroid.nodes.Module):
|
||||
func = '%s.%s' % (i.parent.name, i.name)
|
||||
|
||||
if not func:
|
||||
@@ -190,7 +191,7 @@ class AnsibleUnwantedChecker(BaseChecker):
|
||||
except astroid.exceptions.InferenceError:
|
||||
pass
|
||||
|
||||
def _check_import(self, node, modname): # type: (astroid.node_classes.Import, str) -> None
|
||||
def _check_import(self, node: astroid.nodes.Import, modname: str) -> None:
|
||||
"""Check the imports on the specified import node."""
|
||||
self._check_module_import(node, modname)
|
||||
|
||||
@@ -202,7 +203,7 @@ class AnsibleUnwantedChecker(BaseChecker):
|
||||
if entry.applies_to(self.linter.current_file):
|
||||
self.add_message(self.BAD_IMPORT, args=(entry.alternative, modname), node=node)
|
||||
|
||||
def _check_importfrom(self, node, modname, names): # type: (astroid.node_classes.ImportFrom, str, t.List[str]) -> None
|
||||
def _check_importfrom(self, node: astroid.nodes.ImportFrom, modname: str, names: list[tuple[str, str | None]]) -> None:
|
||||
"""Check the imports on the specified import from node."""
|
||||
self._check_module_import(node, modname)
|
||||
|
||||
@@ -215,7 +216,7 @@ class AnsibleUnwantedChecker(BaseChecker):
|
||||
if entry.applies_to(self.linter.current_file, name[0]):
|
||||
self.add_message(self.BAD_IMPORT_FROM, args=(name[0], entry.alternative, modname), node=node)
|
||||
|
||||
def _check_module_import(self, node, modname): # type: (t.Union[astroid.node_classes.Import, astroid.node_classes.ImportFrom], str) -> None
|
||||
def _check_module_import(self, node: astroid.nodes.Import | astroid.nodes.ImportFrom, modname: str) -> None:
|
||||
"""Check the module import on the given import or import from node."""
|
||||
if not is_module_path(self.linter.current_file):
|
||||
return
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# edit "black.requirements.in" and generate with: hacking/update-sanity-requirements.py --test black
|
||||
black==25.1.0
|
||||
click==8.2.1
|
||||
black==25.9.0
|
||||
click==8.3.0
|
||||
mypy_extensions==1.1.0
|
||||
packaging==25.0
|
||||
pathspec==0.12.1
|
||||
platformdirs==4.4.0
|
||||
platformdirs==4.5.0
|
||||
pytokens==0.1.10
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# edit "deprecated-config.requirements.in" and generate with: hacking/update-sanity-requirements.py --test deprecated-config
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
PyYAML==6.0.2
|
||||
MarkupSafe==3.0.3
|
||||
PyYAML==6.0.3
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
# edit "mypy.requirements.in" and generate with: hacking/update-sanity-requirements.py --test mypy
|
||||
cffi==2.0.0
|
||||
cryptography==45.0.7
|
||||
cryptography==46.0.2
|
||||
iniconfig==2.1.0
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
mypy==1.17.1
|
||||
MarkupSafe==3.0.3
|
||||
mypy==1.18.2
|
||||
mypy_extensions==1.1.0
|
||||
packaging==25.0
|
||||
pathspec==0.12.1
|
||||
pluggy==1.6.0
|
||||
pycparser==2.22
|
||||
pycparser==2.23
|
||||
Pygments==2.19.2
|
||||
pytest==8.4.2
|
||||
pytest-mock==3.15.0
|
||||
resolvelib==1.2.0
|
||||
tomli==2.2.1
|
||||
pytest-mock==3.15.1
|
||||
resolvelib==1.2.1
|
||||
tomli==2.3.0
|
||||
types-backports==0.1.3
|
||||
types-paramiko==4.0.0.20250822
|
||||
types-PyYAML==6.0.12.20250822
|
||||
types-requests==2.32.4.20250809
|
||||
types-PyYAML==6.0.12.20250915
|
||||
types-requests==2.32.4.20250913
|
||||
types-setuptools==80.9.0.20250822
|
||||
types-toml==0.10.8.20240310
|
||||
typing_extensions==4.15.0
|
||||
|
||||
@@ -36,10 +36,19 @@ ignore_missing_imports = True
|
||||
[mypy-astroid]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-astroid.bases]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-astroid.exceptions]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-astroid.nodes]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-astroid.typing]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-astroid.context]
|
||||
[mypy-astroid.util]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-pylint]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# edit "pymarkdown.requirements.in" and generate with: hacking/update-sanity-requirements.py --test pymarkdown
|
||||
application_properties==0.9.0
|
||||
Columnar==1.4.1
|
||||
pyjson5==1.6.9
|
||||
pyjson5==2.0.0
|
||||
pymarkdownlnt==0.9.32
|
||||
PyYAML==6.0.2
|
||||
tomli==2.2.1
|
||||
PyYAML==6.0.3
|
||||
tomli==2.3.0
|
||||
toolz==1.0.0
|
||||
typing_extensions==4.15.0
|
||||
wcwidth==0.2.13
|
||||
wcwidth==0.2.14
|
||||
|
||||
@@ -65,7 +65,6 @@ test/integration/targets/ansible-doc/library/facts_one shebang
|
||||
test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py pylint:ansible-bad-function # ignore, required for testing
|
||||
test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import-from # ignore, required for testing
|
||||
test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/tests/integration/targets/hello/files/bad.py pylint:ansible-bad-import # ignore, required for testing
|
||||
test/integration/targets/ansible-test-sanity/ansible_collections/ns/col/plugins/plugin_utils/check_pylint.py pylint:disallowed-name # ignore, required for testing
|
||||
test/integration/targets/ansible-test-integration/ansible_collections/ns/col/plugins/modules/hello.py pylint:relative-beyond-top-level
|
||||
test/integration/targets/ansible-test-units/ansible_collections/ns/col/plugins/modules/hello.py pylint:relative-beyond-top-level
|
||||
test/integration/targets/ansible-test-units/ansible_collections/ns/col/tests/unit/plugins/modules/test_hello.py pylint:relative-beyond-top-level
|
||||
@@ -115,6 +114,11 @@ test/integration/targets/win_script/files/test_script_with_args.ps1 pslint:PSAvo
|
||||
test/integration/targets/win_script/files/test_script_with_splatting.ps1 pslint:PSAvoidUsingWriteHost # Keep
|
||||
test/integration/targets/ssh_agent/fake_agents/ssh-agent-bad-shebang shebang # required for test
|
||||
test/lib/ansible_test/_data/requirements/sanity.pslint.ps1 pslint:PSCustomUseLiteralPath # Uses wildcards on purpose
|
||||
test/lib/ansible_test/_internal/compat/packaging.py pylint:invalid-name # pylint bug: https://github.com/pylint-dev/pylint/issues/10652
|
||||
test/lib/ansible_test/_internal/compat/yaml.py pylint:invalid-name # pylint bug: https://github.com/pylint-dev/pylint/issues/10652
|
||||
test/lib/ansible_test/_internal/init.py pylint:invalid-name # pylint bug: https://github.com/pylint-dev/pylint/issues/10652
|
||||
test/lib/ansible_test/_internal/util.py pylint:invalid-name # pylint bug: https://github.com/pylint-dev/pylint/issues/10652
|
||||
test/lib/ansible_test/_util/target/setup/requirements.py pylint:invalid-name # pylint bug: https://github.com/pylint-dev/pylint/issues/10652
|
||||
test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/module_utils/WebRequest.psm1 pslint!skip
|
||||
test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/modules/win_uri.ps1 pslint!skip
|
||||
test/support/windows-integration/plugins/modules/async_status.ps1 pslint!skip
|
||||
|
||||
Reference in New Issue
Block a user