Files
community.crypto/plugins/module_utils/_argspec.py
2026-04-26 14:44:44 +02:00

204 lines
7.6 KiB
Python

# Copyright (c) 2020, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time.
# Do not use this from other collections or standalone plugins/modules!
from __future__ import annotations
import typing as t
from ansible.module_utils.basic import AnsibleModule
if t.TYPE_CHECKING:
import datetime # pragma: no cover
from collections.abc import Callable, Mapping, Sequence # pragma: no cover
_T = t.TypeVar("_T") # pragma: no cover
ArgSpecType = t.Literal[ # pragma: no cover
"bits",
"bool",
"bytes",
"dict",
"float",
"int",
"json",
"jsonarg",
"list",
"path",
"raw",
"sid",
"str",
]
MutuallyExclusiveT = t.Union[ # pragma: no cover # noqa: UP007
Sequence[str], Sequence[Sequence[str]]
]
MutuallyExclusiveMutT = list[Sequence[str]] # pragma: no cover
RequiredTogetherT = Sequence[Sequence[str]] # pragma: no cover
RequiredTogetherMutT = list[Sequence[str]] # pragma: no cover
RequiredOneOfT = Sequence[Sequence[str]] # pragma: no cover
RequiredOneOfMutT = list[Sequence[str]] # pragma: no cover
RequiredIfT = Sequence[ # pragma: no cover
t.Union[ # noqa: UP007
list[object],
tuple[str, object, Sequence[str]],
tuple[str, object, Sequence[str], bool],
]
]
RequiredIfMutT = list[ # pragma: no cover
t.Union[ # noqa: UP007
list[object],
tuple[str, object, Sequence[str]],
tuple[str, object, Sequence[str], bool],
]
]
RequiredByT = Mapping[str, Sequence[str]] # pragma: no cover
RequiredByMutT = dict[str, Sequence[str]] # pragma: no cover
class DeprecatedAlias(t.TypedDict): # pragma: no cover
name: str
date: t.NotRequired[datetime.date | str]
version: t.NotRequired[str]
collection_name: str
class OneArgumentSpecT(t.TypedDict): # pragma: no cover
type: t.NotRequired[ArgSpecType | Callable[[object], object]]
elements: t.NotRequired[ArgSpecType]
default: t.NotRequired[object]
# For fallback elements, the first element of the sequence has to be a callable, the others sequences or dicts.
# Unfortunately there is no way to specify this in a generic way...
fallback: t.NotRequired[
Sequence[
Callable[[object], object] | Sequence[object] | Mapping[str, object]
]
]
choices: t.NotRequired[Sequence[object]]
context: t.NotRequired[Mapping[object, object]]
required: t.NotRequired[bool]
no_log: t.NotRequired[bool]
aliases: t.NotRequired[Sequence[str]]
apply_defaults: t.NotRequired[bool]
removed_in_version: t.NotRequired[str]
removed_at_date: t.NotRequired[datetime.date | str]
removed_from_collection: t.NotRequired[str]
options: t.NotRequired[Mapping[str, OneArgumentSpecT]] # recursive!
deprecated_aliases: t.NotRequired[Sequence[DeprecatedAlias]]
mutually_exclusive: t.NotRequired[MutuallyExclusiveT]
required_together: t.NotRequired[RequiredTogetherT]
required_one_of: t.NotRequired[RequiredOneOfT]
required_if: t.NotRequired[RequiredIfT]
required_by: t.NotRequired[RequiredByT]
ArgumentSpecT = Mapping[str, OneArgumentSpecT] # pragma: no cover
ArgumentSpecMutT = dict[str, OneArgumentSpecT] # pragma: no cover
class ArgumentSpec:
def __init__(
self,
argument_spec: ArgumentSpecT | None = None,
*,
required_together: RequiredTogetherT | None = None,
required_if: RequiredIfT | None = None,
required_one_of: RequiredOneOfT | None = None,
mutually_exclusive: MutuallyExclusiveT | None = None,
required_by: RequiredByT | None = None,
) -> None:
self.argument_spec: ArgumentSpecMutT = {}
self.required_together: RequiredTogetherMutT = []
self.required_if: RequiredIfMutT = []
self.required_one_of: RequiredOneOfMutT = []
self.mutually_exclusive: MutuallyExclusiveMutT = []
self.required_by: RequiredByMutT = {}
if argument_spec:
self.argument_spec.update(argument_spec)
if required_together:
self.required_together.extend(required_together)
if required_if:
self.required_if.extend(required_if)
if required_one_of:
self.required_one_of.extend(required_one_of)
if mutually_exclusive:
if all(isinstance(me, str) for me in mutually_exclusive):
# mutually_exclusive is a Sequence[str]
self.mutually_exclusive.append(mutually_exclusive) # type: ignore
else:
self.mutually_exclusive.extend(mutually_exclusive)
if required_by:
self.required_by.update(required_by)
def update_argspec(self, **kwargs: t.Any) -> t.Self:
self.argument_spec.update(kwargs)
return self
def update(
self,
*,
required_together: RequiredTogetherT | None = None,
required_if: RequiredIfT | None = None,
required_one_of: RequiredOneOfT | None = None,
mutually_exclusive: MutuallyExclusiveT | None = None,
required_by: RequiredByT | None = None,
) -> t.Self:
if mutually_exclusive:
self.mutually_exclusive.extend(mutually_exclusive)
if required_together:
self.required_together.extend(required_together)
if required_one_of:
self.required_one_of.extend(required_one_of)
if required_if:
self.required_if.extend(required_if)
if required_by:
for k, v in required_by.items():
if k in self.required_by:
v = list(self.required_by[k]) + list(v)
self.required_by[k] = v
return self
def merge(self, other: t.Self) -> t.Self:
self.update_argspec(**other.argument_spec)
self.update(
mutually_exclusive=other.mutually_exclusive,
required_together=other.required_together,
required_one_of=other.required_one_of,
required_if=other.required_if,
required_by=other.required_by,
)
return self
def create_ansible_module_helper(
self, clazz: type[_T], args: tuple, **kwargs: t.Any
) -> _T:
for forbidden_name in (
"argument_spec",
"mutually_exclusive",
"required_together",
"required_one_of",
"required_if",
"required_by",
):
if forbidden_name in kwargs:
raise ValueError(
f"You must not provide a {forbidden_name} keyword parameter to create_ansible_module_helper()"
)
instance = clazz( # type: ignore
*args,
argument_spec=self.argument_spec,
mutually_exclusive=self.mutually_exclusive,
required_together=self.required_together,
required_one_of=self.required_one_of,
required_if=self.required_if,
required_by=self.required_by,
**kwargs,
)
return instance
def create_ansible_module(self, **kwargs: t.Any) -> AnsibleModule:
return self.create_ansible_module_helper(AnsibleModule, (), **kwargs)
__all__ = ("ArgumentSpec",)