Commits vergleichen
5 Commits
Autor | SHA1 | Datum | |
---|---|---|---|
3bb2a93dab | |||
749ae48441 | |||
d82a902043 | |||
328e58c439 | |||
9045e51c23 |
9 geänderte Dateien mit 442 neuen und 138 gelöschten Zeilen
1
.gitignore
gevendort
1
.gitignore
gevendort
|
@ -164,3 +164,4 @@ cython_debug/
|
||||||
# ---> Ansible
|
# ---> Ansible
|
||||||
*.retry
|
*.retry
|
||||||
|
|
||||||
|
src/ansible_module/module_utils
|
||||||
|
|
2
changelogs/fragments/0.4.5.yml
Normale Datei
2
changelogs/fragments/0.4.5.yml
Normale Datei
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- Added ansible as an dependency for the python module
|
|
@ -1,6 +1,6 @@
|
||||||
namespace: sebastian
|
namespace: sebastian
|
||||||
name: base
|
name: base
|
||||||
version: 0.4.4
|
version: 0.5.0
|
||||||
readme: README.md
|
readme: README.md
|
||||||
authors:
|
authors:
|
||||||
- Sebastian Tobie
|
- Sebastian Tobie
|
||||||
|
|
|
@ -1,30 +1,69 @@
|
||||||
import builtins
|
import builtins
|
||||||
import pathlib
|
import pathlib
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Any, Dict, Optional, Sequence, Tuple, Type, Union
|
from typing import Any, Dict, Literal, NotRequired, Optional, Required, Sequence, Tuple, Type, TypedDict
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"Types",
|
"Types",
|
||||||
"SYSTEMD_SERVICE_CONFIG",
|
"SYSTEMD_SERVICE_CONFIG",
|
||||||
"SYSTEMD_NETWORK_CONFIG",
|
"SYSTEMD_NETWORK_CONFIG",
|
||||||
"SYSTEMD_CONFIG_ROOT",
|
"SYSTEMD_CONFIG_ROOT",
|
||||||
|
"FILE_COMMON_ARGS",
|
||||||
"systemdbool",
|
"systemdbool",
|
||||||
"AnsibleParameter",
|
"AnsibleParameter",
|
||||||
|
"AnsibleReturnParameter",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
SYSTEMD_CONFIG_ROOT = pathlib.Path("/etc/systemd")
|
SYSTEMD_CONFIG_ROOT = pathlib.Path("/etc/systemd")
|
||||||
SYSTEMD_NETWORK_CONFIG = SYSTEMD_CONFIG_ROOT / "network"
|
SYSTEMD_NETWORK_CONFIG = SYSTEMD_CONFIG_ROOT / "network"
|
||||||
SYSTEMD_SERVICE_CONFIG = SYSTEMD_CONFIG_ROOT / "system"
|
SYSTEMD_SERVICE_CONFIG = SYSTEMD_CONFIG_ROOT / "system"
|
||||||
|
FILE_COMMON_ARGS = frozenset(("owner", "group", "mode", "seuser", "serole", "setype", "selevel", "unsafe_writes"))
|
||||||
|
|
||||||
AnsibleParameter = Dict[str, Any]
|
AnsibleType = Literal[
|
||||||
|
"str",
|
||||||
|
"bool",
|
||||||
|
"int",
|
||||||
|
"float",
|
||||||
|
"path",
|
||||||
|
"raw",
|
||||||
|
"jsonarg",
|
||||||
|
"json",
|
||||||
|
"bytes",
|
||||||
|
"dict",
|
||||||
|
"list",
|
||||||
|
"bits",
|
||||||
|
]
|
||||||
|
ReturnOptions = Literal["always", "changed", "success"]
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleParameter(TypedDict, total=False):
|
||||||
|
description: Required[str | list[str]]
|
||||||
|
required: NotRequired[bool]
|
||||||
|
default: NotRequired[Any]
|
||||||
|
type: Required[AnsibleType]
|
||||||
|
choices: NotRequired[list[Any] | tuple[Any]]
|
||||||
|
elements: NotRequired["AnsibleParameter" | AnsibleType]
|
||||||
|
aliases: NotRequired[list[str]]
|
||||||
|
version_added: NotRequired[str]
|
||||||
|
options: NotRequired[dict[str, "AnsibleParameter"]]
|
||||||
|
|
||||||
|
|
||||||
|
class AnsibleReturnParameter(TypedDict, total=False):
|
||||||
|
description: Required[str | list[str]]
|
||||||
|
type: Required[AnsibleType]
|
||||||
|
returned: NotRequired[ReturnOptions]
|
||||||
|
elements: NotRequired[AnsibleType]
|
||||||
|
sample: NotRequired[list[Any] | Any]
|
||||||
|
version_added: NotRequired[str]
|
||||||
|
contains: NotRequired[dict[str, "AnsibleReturnParameter"]]
|
||||||
|
|
||||||
|
|
||||||
def wrap_func(func, **updates):
|
def wrap_func(func, **updates):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
attrs = set(('__module__', '__name__', '__qualname__', '__doc__', '__annotations__', '__type_params__')).difference(
|
attrs = {'__module__', '__name__', '__qualname__', '__doc__', '__annotations__', '__type_params__'}.difference(
|
||||||
updates.keys()
|
updates.keys()
|
||||||
)
|
)
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
|
@ -44,29 +83,14 @@ def wrap_func(func, **updates):
|
||||||
GENERIC_DOC = """Returns an dictionary for the Ansible {type} type."""
|
GENERIC_DOC = """Returns an dictionary for the Ansible {type} type."""
|
||||||
|
|
||||||
|
|
||||||
def default(name: str):
|
class TypeBase(type):
|
||||||
def wrapped(
|
|
||||||
required: bool = False,
|
|
||||||
help: str | list[str] | None = None,
|
|
||||||
choices: Optional[Sequence] = None,
|
|
||||||
default: Optional[Any] = None,
|
|
||||||
) -> AnsibleParameter:
|
|
||||||
option: AnsibleParameter = dict(type=name, required=required)
|
|
||||||
if choices is not None:
|
|
||||||
option["choices"] = choices
|
|
||||||
if default is not None:
|
|
||||||
option["default"] = default
|
|
||||||
if help is not None and isinstance(help, str):
|
|
||||||
option["description"] = help.split("\n")
|
|
||||||
elif help is not None:
|
|
||||||
option["description"] = help
|
|
||||||
return option
|
|
||||||
|
|
||||||
return wrapped
|
|
||||||
|
|
||||||
|
|
||||||
class meta(type):
|
|
||||||
def __new__(cls, clsname, bases, attrs):
|
def __new__(cls, clsname, bases, attrs):
|
||||||
|
if "_default" not in attrs:
|
||||||
|
raise TypeError(
|
||||||
|
f"The class {clsname} must define an wrapper function called _default that returns the default type"
|
||||||
|
)
|
||||||
|
default_type = attrs["_default"]
|
||||||
|
del attrs["_default"]
|
||||||
types = frozenset(
|
types = frozenset(
|
||||||
(
|
(
|
||||||
"str",
|
"str",
|
||||||
|
@ -85,19 +109,38 @@ class meta(type):
|
||||||
)
|
)
|
||||||
for attr in types - set(attrs.keys()):
|
for attr in types - set(attrs.keys()):
|
||||||
attrs[attr] = wrap_func(
|
attrs[attr] = wrap_func(
|
||||||
default(attr), __doc__=GENERIC_DOC.format(type=attr), __name__=attr, __qualname__=f"{clsname}.{attr}"
|
default_type(attr),
|
||||||
|
__doc__=GENERIC_DOC.format(type=attr),
|
||||||
|
__name__=attr,
|
||||||
|
__qualname__=f"{clsname}.{attr}",
|
||||||
)
|
)
|
||||||
attrs["__slots__"] = ()
|
attrs["__slots__"] = ()
|
||||||
return super().__new__(cls, clsname, bases, attrs)
|
return super().__new__(cls, clsname, bases, attrs)
|
||||||
|
|
||||||
|
|
||||||
class Types(metaclass=meta):
|
class Types(metaclass=TypeBase):
|
||||||
|
@staticmethod
|
||||||
|
def _default(name: AnsibleType):
|
||||||
|
def wrapped(
|
||||||
|
help: str | list[str],
|
||||||
|
required: bool = False,
|
||||||
|
choices: Optional[Sequence] = None,
|
||||||
|
default: Optional[Any] = None,
|
||||||
|
) -> AnsibleParameter:
|
||||||
|
option = AnsibleParameter(type=name, required=required, description=help)
|
||||||
|
if choices is not None:
|
||||||
|
option["choices"] = tuple(choices)
|
||||||
|
if default is not None:
|
||||||
|
option["default"] = default
|
||||||
|
return option
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list( # type: ignore[misc]
|
def list( # type: ignore[misc]
|
||||||
elements: Union[Type[object], str, AnsibleParameter],
|
elements: Type[object] | AnsibleType | AnsibleParameter,
|
||||||
|
help: str | list[str],
|
||||||
required: bool = False,
|
required: bool = False,
|
||||||
help: str | list[str] | None = None,
|
|
||||||
default: list[Any] | None = None,
|
default: list[Any] | None = None,
|
||||||
) -> AnsibleParameter:
|
) -> AnsibleParameter:
|
||||||
"""Wrapper for the Ansible list type
|
"""Wrapper for the Ansible list type
|
||||||
|
@ -110,12 +153,16 @@ class Types(metaclass=meta):
|
||||||
"""
|
"""
|
||||||
if required and default:
|
if required and default:
|
||||||
raise ValueError("required and default are not allowed")
|
raise ValueError("required and default are not allowed")
|
||||||
option: AnsibleParameter = dict(type="list", required=required)
|
option: AnsibleParameter = AnsibleParameter(
|
||||||
|
type="list",
|
||||||
|
required=required,
|
||||||
|
description=help,
|
||||||
|
)
|
||||||
if not isinstance(elements, (str, dict)):
|
if not isinstance(elements, (str, dict)):
|
||||||
option["elements"] = elements.__name__
|
option["elements"] = elements.__name__ # type:ignore[reportGeneralTypeIssue]
|
||||||
elif isinstance(elements, dict):
|
elif isinstance(elements, dict):
|
||||||
option["elements"] = elements["type"]
|
option["elements"] = elements["type"]
|
||||||
if elements["type"] == "dict":
|
if elements["type"] == "dict" and "options" in elements:
|
||||||
option["options"] = dict()
|
option["options"] = dict()
|
||||||
for name, value in elements["options"].items():
|
for name, value in elements["options"].items():
|
||||||
option["options"][name] = value
|
option["options"][name] = value
|
||||||
|
@ -124,9 +171,8 @@ class Types(metaclass=meta):
|
||||||
f"helptext of option {name} is unset."
|
f"helptext of option {name} is unset."
|
||||||
" Ansible requires suboptions to have an documentation"
|
" Ansible requires suboptions to have an documentation"
|
||||||
)
|
)
|
||||||
elif elements["type"] == "list":
|
elif "choices" in elements:
|
||||||
if "choices" in elements:
|
option["choices"] = elements["choices"]
|
||||||
option["choices"] = elements["choices"]
|
|
||||||
if default is not None:
|
if default is not None:
|
||||||
option["default"] = default
|
option["default"] = default
|
||||||
if help is not None and isinstance(help, str):
|
if help is not None and isinstance(help, str):
|
||||||
|
@ -136,7 +182,7 @@ class Types(metaclass=meta):
|
||||||
return option
|
return option
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def dict(required: bool = False, help: str | builtins.list[str] | None = None, **options: AnsibleParameter) -> AnsibleParameter: # type: ignore[misc]
|
def dict(help: str | builtins.list[str], required: bool = False, **options: AnsibleParameter) -> AnsibleParameter: # type: ignore[misc]
|
||||||
"""Wrapper for the Ansible dict type
|
"""Wrapper for the Ansible dict type
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -144,7 +190,7 @@ class Types(metaclass=meta):
|
||||||
help: an helptext for the ansible-doc
|
help: an helptext for the ansible-doc
|
||||||
options: The individual options that this parameter has
|
options: The individual options that this parameter has
|
||||||
"""
|
"""
|
||||||
option: AnsibleParameter = dict(type="dict", required=required)
|
option: AnsibleParameter = AnsibleParameter(type="dict", description=help, required=required)
|
||||||
option["options"] = options
|
option["options"] = options
|
||||||
if help is not None and isinstance(help, str):
|
if help is not None and isinstance(help, str):
|
||||||
option["description"] = help.split("\n")
|
option["description"] = help.split("\n")
|
||||||
|
@ -153,7 +199,70 @@ class Types(metaclass=meta):
|
||||||
return option
|
return option
|
||||||
|
|
||||||
|
|
||||||
def systemdbool(b: Union[bool, str]) -> str:
|
class ReturnTypes(metaclass=TypeBase):
|
||||||
|
@staticmethod
|
||||||
|
def _default(name: AnsibleType):
|
||||||
|
def wrapped(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
) -> AnsibleReturnParameter:
|
||||||
|
option = AnsibleReturnParameter(type=name, description=help)
|
||||||
|
if returned is not None:
|
||||||
|
option["returned"] = returned
|
||||||
|
if sample is not None:
|
||||||
|
option["sample"] = sample
|
||||||
|
if version_added is not None:
|
||||||
|
option["version_added"] = version_added
|
||||||
|
return option
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def list(
|
||||||
|
help: str | list[str],
|
||||||
|
elements: AnsibleType | AnsibleReturnParameter | Type[object],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: builtins.list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
) -> AnsibleReturnParameter:
|
||||||
|
option = AnsibleReturnParameter(description=help, type="list")
|
||||||
|
if returned is not None:
|
||||||
|
option["returned"] = returned
|
||||||
|
if isinstance(elements, str):
|
||||||
|
option["elements"] = elements
|
||||||
|
elif isinstance(elements, dict):
|
||||||
|
option["elements"] = elements["type"]
|
||||||
|
if "contains" in elements:
|
||||||
|
option["contains"] = elements["contains"]
|
||||||
|
else:
|
||||||
|
option["elements"] = elements.__name__ # type:ignore[reportGeneralTypeIssue]
|
||||||
|
if sample is not None:
|
||||||
|
option["sample"] = sample
|
||||||
|
if version_added is not None:
|
||||||
|
option["version_added"] = version_added
|
||||||
|
return option
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def dict(
|
||||||
|
help: str | builtins.list[str],
|
||||||
|
contains: dict[str, AnsibleReturnParameter],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: builtins.list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
):
|
||||||
|
option = AnsibleReturnParameter(description=help, type="dict", contains=contains)
|
||||||
|
if returned is not None:
|
||||||
|
option["contains"] = contains
|
||||||
|
if sample is not None:
|
||||||
|
option["sample"] = sample
|
||||||
|
if version_added is not None:
|
||||||
|
option["version_added"] = version_added
|
||||||
|
return option
|
||||||
|
|
||||||
|
|
||||||
|
def systemdbool(b: bool | str) -> str:
|
||||||
"""Converts values into things systemd can parse"""
|
"""Converts values into things systemd can parse"""
|
||||||
if b is True:
|
if b is True:
|
||||||
return "yes"
|
return "yes"
|
||||||
|
@ -167,10 +276,14 @@ def modspec(
|
||||||
mutually_exclusive: Sequence[Tuple[str, ...]] = (),
|
mutually_exclusive: Sequence[Tuple[str, ...]] = (),
|
||||||
required_together: Sequence[Tuple[str, ...]] = (),
|
required_together: Sequence[Tuple[str, ...]] = (),
|
||||||
required_one_of: Sequence[Tuple[str, ...]] = (),
|
required_one_of: Sequence[Tuple[str, ...]] = (),
|
||||||
required_if: Sequence[Union[Tuple[str, Any, Tuple[str, ...]], Tuple[str, Any, Tuple[str, ...], bool]]] = (),
|
required_if: Sequence[Tuple[str, Any, Tuple[str, ...]] | Tuple[str, Any, Tuple[str, ...], bool]] = (),
|
||||||
required_by: Dict[str, Union[str, Tuple[str, ...]]] = {},
|
required_by: Dict[str, str | Tuple[str, ...]] = {},
|
||||||
supports_check_mode: bool = False,
|
supports_check_mode: bool = False,
|
||||||
add_file_common_args: bool = False,
|
add_file_common_args: bool = False,
|
||||||
|
deprecated: bool = False,
|
||||||
|
version_added: str | None = None,
|
||||||
|
notes: list[str] | None = None,
|
||||||
|
extends_documentation_fragment: list[str] | None = None,
|
||||||
) -> Dict[str, Any]: # pragma: nocover
|
) -> Dict[str, Any]: # pragma: nocover
|
||||||
"""Wrapper to properly Type the module specs"""
|
"""Wrapper to properly Type the module specs"""
|
||||||
return dict(
|
return dict(
|
||||||
|
@ -182,6 +295,10 @@ def modspec(
|
||||||
required_by=required_by,
|
required_by=required_by,
|
||||||
add_file_common_args=add_file_common_args,
|
add_file_common_args=add_file_common_args,
|
||||||
supports_check_mode=supports_check_mode,
|
supports_check_mode=supports_check_mode,
|
||||||
|
deprecated=deprecated,
|
||||||
|
version_added=version_added,
|
||||||
|
notes=notes,
|
||||||
|
extends_documentation_fragment=extends_documentation_fragment,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,62 @@
|
||||||
from pathlib import PosixPath
|
from pathlib import PosixPath
|
||||||
from typing import Any, Dict, Optional, Sequence, Tuple, Union
|
from typing import Any, Dict, Literal, NotRequired, Optional, Required, Sequence, Tuple, Type, TypedDict
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Types',
|
"Types",
|
||||||
'SYSTEMD_SERVICE_CONFIG',
|
"SYSTEMD_SERVICE_CONFIG",
|
||||||
'SYSTEMD_NETWORK_CONFIG',
|
"SYSTEMD_NETWORK_CONFIG",
|
||||||
'SYSTEMD_CONFIG_ROOT',
|
"SYSTEMD_CONFIG_ROOT",
|
||||||
'systemdbool',
|
"FILE_COMMON_ARGS",
|
||||||
'AnsibleParameter',
|
"systemdbool",
|
||||||
|
"AnsibleParameter",
|
||||||
|
"AnsibleReturnParameter",
|
||||||
]
|
]
|
||||||
|
|
||||||
SYSTEMD_CONFIG_ROOT: PosixPath
|
SYSTEMD_CONFIG_ROOT: PosixPath
|
||||||
SYSTEMD_NETWORK_CONFIG: PosixPath
|
SYSTEMD_NETWORK_CONFIG: PosixPath
|
||||||
SYSTEMD_SERVICE_CONFIG: PosixPath
|
SYSTEMD_SERVICE_CONFIG: PosixPath
|
||||||
AnsibleParameter = dict[str, Any]
|
FILE_COMMON_ARGS: frozenset[str]
|
||||||
|
|
||||||
class meta(type):
|
AnsibleType = Literal[
|
||||||
|
"str",
|
||||||
|
"bool",
|
||||||
|
"int",
|
||||||
|
"float",
|
||||||
|
"path",
|
||||||
|
"raw",
|
||||||
|
"jsonarg",
|
||||||
|
"json",
|
||||||
|
"bytes",
|
||||||
|
"dict",
|
||||||
|
"list",
|
||||||
|
"bits",
|
||||||
|
]
|
||||||
|
ReturnOptions = Literal["always", "changed", "success"]
|
||||||
|
|
||||||
|
class AnsibleParameter(TypedDict, total=False):
|
||||||
|
description: Required[str | list[str]]
|
||||||
|
required: NotRequired[bool]
|
||||||
|
default: NotRequired[Any]
|
||||||
|
type: Required[AnsibleType]
|
||||||
|
choices: NotRequired[list[Any] | tuple[Any]]
|
||||||
|
elements: NotRequired["AnsibleParameter" | AnsibleType]
|
||||||
|
aliases: NotRequired[list[str]]
|
||||||
|
version_added: NotRequired[str]
|
||||||
|
options: NotRequired[dict[str, "AnsibleParameter"]]
|
||||||
|
|
||||||
|
class AnsibleReturnParameter(TypedDict, total=False):
|
||||||
|
description: Required[str | list[str]]
|
||||||
|
type: Required[AnsibleType]
|
||||||
|
returned: NotRequired[ReturnOptions]
|
||||||
|
elements: NotRequired[AnsibleType]
|
||||||
|
sample: NotRequired[list[Any] | Any]
|
||||||
|
version_added: NotRequired[str]
|
||||||
|
contains: NotRequired[dict[str, "AnsibleReturnParameter"]]
|
||||||
|
|
||||||
|
class TypeBase(type):
|
||||||
def __new__(cls, clsname, bases, attrs): ...
|
def __new__(cls, clsname, bases, attrs): ...
|
||||||
|
|
||||||
class Types(metaclass=meta):
|
class Types(metaclass=TypeBase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def str(
|
def str(
|
||||||
required: bool = False,
|
required: bool = False,
|
||||||
|
@ -101,6 +139,94 @@ class Types(metaclass=meta):
|
||||||
required: bool = False, help: str | list[str] | None = None, **options: AnsibleParameter
|
required: bool = False, help: str | list[str] | None = None, **options: AnsibleParameter
|
||||||
) -> AnsibleParameter: ...
|
) -> AnsibleParameter: ...
|
||||||
|
|
||||||
|
class ReturnTypes(metaclass=TypeBase):
|
||||||
|
@staticmethod
|
||||||
|
def list(
|
||||||
|
help: str | list[str],
|
||||||
|
elements: AnsibleType | AnsibleReturnParameter | Type[object],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def dict(
|
||||||
|
help: str | list[str],
|
||||||
|
contains: dict[str, AnsibleReturnParameter],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def str(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def bool(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def int(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def float(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def path(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def raw(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def jsonarg(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def json(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def bytes(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
@staticmethod
|
||||||
|
def bits(
|
||||||
|
help: str | list[str],
|
||||||
|
returned: ReturnOptions | None = None,
|
||||||
|
sample: list[Any] | None = None,
|
||||||
|
version_added: str | None = None,
|
||||||
|
): ...
|
||||||
|
|
||||||
def systemdbool(b: bool | str) -> str: ...
|
def systemdbool(b: bool | str) -> str: ...
|
||||||
def joindict(*items: dict) -> dict: ...
|
def joindict(*items: dict) -> dict: ...
|
||||||
def modspec(
|
def modspec(
|
||||||
|
@ -108,8 +234,12 @@ def modspec(
|
||||||
mutually_exclusive: Sequence[Tuple[str, ...]] = (),
|
mutually_exclusive: Sequence[Tuple[str, ...]] = (),
|
||||||
required_together: Sequence[Tuple[str, ...]] = (),
|
required_together: Sequence[Tuple[str, ...]] = (),
|
||||||
required_one_of: Sequence[Tuple[str, ...]] = (),
|
required_one_of: Sequence[Tuple[str, ...]] = (),
|
||||||
required_if: Sequence[Union[Tuple[str, Any, Tuple[str, ...]], Tuple[str, Any, Tuple[str, ...], bool]]] = (),
|
required_if: Sequence[Tuple[str, Any, Tuple[str, ...]] | Tuple[str, Any, Tuple[str, ...], bool]] = (),
|
||||||
required_by: Dict[str, Union[str, Tuple[str, ...]]] = {},
|
required_by: Dict[str, str | Tuple[str, ...]] = {},
|
||||||
supports_check_mode: bool = False,
|
supports_check_mode: bool = False,
|
||||||
add_file_common_args: bool = False,
|
add_file_common_args: bool = False,
|
||||||
|
deprecated: bool = False,
|
||||||
|
version_added: str | None = None,
|
||||||
|
notes: list[str] | None = None,
|
||||||
|
extends_documentation_fragment: list[str] | None = None,
|
||||||
) -> Dict[str, Any]: ...
|
) -> Dict[str, Any]: ...
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import shutil
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import (Any, Callable, ClassVar, Dict, NoReturn, Optional, Type,
|
from typing import Any, Callable, ClassVar, Dict, Generic, NoReturn, Optional, Type, TypedDict, TypeVar, overload
|
||||||
TypedDict, TypeVar, Union, overload)
|
|
||||||
|
|
||||||
import ansible.module_utils.basic as basic
|
import ansible.module_utils.basic as basic
|
||||||
|
|
||||||
from .generic import AnsibleParameter, Types, systemdbool
|
from .generic import AnsibleParameter, AnsibleReturnParameter, Types, modspec, systemdbool
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"AnsibleModule",
|
"AnsibleModule",
|
||||||
|
@ -18,35 +19,35 @@ __all__ = (
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
class TypedDiff(TypedDict):
|
class TypedDiff(Generic[T], TypedDict, total=False):
|
||||||
before: str
|
before: T
|
||||||
after: str
|
after: T
|
||||||
before_header: Optional[str]
|
before_header: str
|
||||||
after_header: Optional[str]
|
after_header: str
|
||||||
|
|
||||||
|
|
||||||
def docify(input: Union[dict, AnsibleParameter]) -> dict:
|
def docify(input: AnsibleParameter) -> dict:
|
||||||
options = dict()
|
options = dict()
|
||||||
for name, help in input.items():
|
for name, help in input.items():
|
||||||
options[name] = dict(type=help["type"])
|
options[name] = dict(type=help["type"]) # type: ignore[reportIndexIssue]
|
||||||
if "description" in help:
|
if "description" in help: # type: ignore[reportOperatorIssue]
|
||||||
if isinstance(help["description"], str):
|
if isinstance(help["description"], str): # type: ignore[reportIndexIssue]
|
||||||
help["description"] = help["description"].split("\n")
|
help["description"] = help["description"].split("\n") # type: ignore[reportIndexIssue]
|
||||||
options[name]["description"] = help["description"]
|
options[name]["description"] = help["description"] # type: ignore[reportIndexIssue]
|
||||||
if "required" in help and help["required"]:
|
if "required" in help and help["required"]: # type: ignore[reportOperatorIssue]
|
||||||
options[name]["required"] = True
|
options[name]["required"] = True
|
||||||
else:
|
else:
|
||||||
options[name]["required"] = False
|
options[name]["required"] = False
|
||||||
if help["type"] == "list":
|
if help["type"] == "list": # type: ignore[reportOperatorIssue]
|
||||||
options[name]["elements"] = help["elements"]
|
options[name]["elements"] = help["elements"] # type: ignore[reportIndexIssue]
|
||||||
if not options[name]["required"]:
|
if not options[name]["required"]:
|
||||||
options[name]["default"] = []
|
options[name]["default"] = []
|
||||||
if "default" in help:
|
if "default" in help: # type: ignore[reportOperatorIssue]
|
||||||
options[name]["default"] = help["default"]
|
options[name]["default"] = help["default"] # type: ignore[reportIndexIssue]
|
||||||
if "options" in help and help["options"] != {}:
|
if "options" in help and help["options"] != {}: # type: ignore[reportOperatorIssue]
|
||||||
options[name]["options"] = docify(help["options"])
|
options[name]["options"] = docify(help["options"]) # type: ignore[reportIndexIssue]
|
||||||
if "choices" in help and len(help["choices"]) > 0:
|
if "choices" in help and len(help["choices"]) > 0: # type: ignore[reportOperatorIssue]
|
||||||
options[name]["choices"] = tuple(help["choices"])
|
options[name]["choices"] = tuple(help["choices"]) # type: ignore[reportIndexIssue]
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,6 +62,10 @@ class AnsibleModule(object):
|
||||||
result: dict
|
result: dict
|
||||||
#: the specification of the arguments. Subclasses that are usable Modules must set this value.
|
#: the specification of the arguments. Subclasses that are usable Modules must set this value.
|
||||||
module_spec: ClassVar[dict]
|
module_spec: ClassVar[dict]
|
||||||
|
|
||||||
|
#: The specification of return arguments
|
||||||
|
return_spec: ClassVar[dict[str, AnsibleReturnParameter]]
|
||||||
|
|
||||||
#: This is set by classes that define common things for their subclasses, like behaviour of the run and check methods. This is used by `SystemdUnitModule`
|
#: This is set by classes that define common things for their subclasses, like behaviour of the run and check methods. This is used by `SystemdUnitModule`
|
||||||
_common_args: ClassVar[dict[str, Any]] = dict()
|
_common_args: ClassVar[dict[str, Any]] = dict()
|
||||||
|
|
||||||
|
@ -69,7 +74,11 @@ class AnsibleModule(object):
|
||||||
"""params is an wrapper for the module.params"""
|
"""params is an wrapper for the module.params"""
|
||||||
return self.module.params # type: ignore
|
return self.module.params # type: ignore
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, documentation=False):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
documentation: Only true if the module is initialized for documentation. The Ansible Module is None is this case
|
||||||
|
"""
|
||||||
self.result = dict(changed=False)
|
self.result = dict(changed=False)
|
||||||
specs = dict()
|
specs = dict()
|
||||||
specs.update(deepcopy(self._common_args))
|
specs.update(deepcopy(self._common_args))
|
||||||
|
@ -78,8 +87,13 @@ class AnsibleModule(object):
|
||||||
specs["argument_spec"].update(modspec["argument_spec"])
|
specs["argument_spec"].update(modspec["argument_spec"])
|
||||||
del modspec["argument_spec"]
|
del modspec["argument_spec"]
|
||||||
specs.update(modspec)
|
specs.update(modspec)
|
||||||
self.module = basic.AnsibleModule(**specs)
|
self.modspec = specs.copy()
|
||||||
self.tmpdir = pathlib.Path(self.module.tmpdir)
|
specs.pop("deprecated", None)
|
||||||
|
specs.pop("notes", None)
|
||||||
|
specs.pop("version_added", None)
|
||||||
|
if not documentation:
|
||||||
|
self.module = basic.AnsibleModule(**specs)
|
||||||
|
self.tmpdir = pathlib.Path(self.module.tmpdir)
|
||||||
|
|
||||||
def set(self, key: str, value):
|
def set(self, key: str, value):
|
||||||
"""sets an value for the result"""
|
"""sets an value for the result"""
|
||||||
|
@ -114,7 +128,7 @@ class AnsibleModule(object):
|
||||||
if diff is not None and not any((before is not None, after is not None)):
|
if diff is not None and not any((before is not None, after is not None)):
|
||||||
pass
|
pass
|
||||||
elif all((before is not None, after is not None, diff is None)):
|
elif all((before is not None, after is not None, diff is None)):
|
||||||
diff = dict(
|
diff = TypedDiff(
|
||||||
before=before,
|
before=before,
|
||||||
after=after,
|
after=after,
|
||||||
)
|
)
|
||||||
|
@ -178,21 +192,50 @@ class AnsibleModule(object):
|
||||||
doc = cls.__doc__
|
doc = cls.__doc__
|
||||||
if doc is None:
|
if doc is None:
|
||||||
doc = ""
|
doc = ""
|
||||||
specs = dict()
|
mod = cls(documentation=True)
|
||||||
if "argument_spec" in cls._common_args: # pragma: nocover
|
options = docify(mod.modspec["argument_spec"])
|
||||||
specs.update(cls._common_args["argument_spec"])
|
|
||||||
if "argument_spec" in cls.module_spec: # pragma: nocover
|
|
||||||
specs.update(cls.module_spec["argument_spec"])
|
|
||||||
options = docify(specs)
|
|
||||||
docu = doc.split("\n")
|
docu = doc.split("\n")
|
||||||
|
documentation: dict[str, Any] = dict(
|
||||||
|
module=cls.name,
|
||||||
|
short_description=docu[0],
|
||||||
|
description=docu,
|
||||||
|
options=options,
|
||||||
|
)
|
||||||
|
if mod.modspec.get("extends_documentation_fragment", None) is not None or mod.modspec.get(
|
||||||
|
"add_file_common_args", False
|
||||||
|
):
|
||||||
|
documentation["extends_documentation_fragment"] = []
|
||||||
|
if mod.modspec.get("extends_documentation_fragment", None) is not None:
|
||||||
|
documentation["extends_documentation_fragment"].extend(mod.modspec["extends_documentation_fragment"])
|
||||||
|
if mod.modspec.get("add_file_common_args", False):
|
||||||
|
documentation["extends_documentation_fragment"].append("ansible.builtin.files")
|
||||||
|
if mod.modspec.get("deprecated", False):
|
||||||
|
documentation["deprecated"] = True
|
||||||
|
if mod.modspec.get("notes", None) is not None:
|
||||||
|
documentation["notes"] = mod.modspec["notes"]
|
||||||
|
if mod.modspec.get("version_added", None) is not None:
|
||||||
|
documentation["version_added"] = mod.modspec["version_added"]
|
||||||
|
|
||||||
return str(
|
return str(
|
||||||
yaml.safe_dump(
|
yaml.safe_dump(
|
||||||
dict(
|
documentation,
|
||||||
module=cls.name,
|
stream=None,
|
||||||
short_description=docu[0],
|
explicit_start=True,
|
||||||
description=docu,
|
)
|
||||||
options=options,
|
)
|
||||||
),
|
|
||||||
|
@classmethod
|
||||||
|
def returns(cls) -> str:
|
||||||
|
"""This returns the string for the RETURNS String of the module documentation"""
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
except ImportError: # pragma: nocover
|
||||||
|
return "---\n"
|
||||||
|
if not hasattr(cls, "return_spec"):
|
||||||
|
return "--\n"
|
||||||
|
return str(
|
||||||
|
yaml.safe_dump(
|
||||||
|
cls.returns,
|
||||||
stream=None,
|
stream=None,
|
||||||
explicit_start=True,
|
explicit_start=True,
|
||||||
)
|
)
|
||||||
|
@ -206,6 +249,24 @@ class AnsibleModule(object):
|
||||||
"""Wrapper for AnsibleModule.exit_json"""
|
"""Wrapper for AnsibleModule.exit_json"""
|
||||||
self.module.exit_json(**self.result)
|
self.module.exit_json(**self.result)
|
||||||
|
|
||||||
|
def move_file(self, path: pathlib.Path, dest: pathlib.Path, backup: bool = False, unsafe_writes: bool = False):
|
||||||
|
"""Moves an Temporary file to an destination it uses the args from add_file_common_args when used
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: The Path that the file currently is
|
||||||
|
dest: the location the file should be
|
||||||
|
backup: should an backup be made before the file is moved
|
||||||
|
"""
|
||||||
|
|
||||||
|
if backup:
|
||||||
|
shutil.copy2(dest, dest.with_suffix(dest.suffix + ".bak"), follow_symlinks=False)
|
||||||
|
self.module.atomic_move(path, dest, unsafe_writes=unsafe_writes, keep_dest_attrs=True)
|
||||||
|
if "add_file_common_args" in self.modspec and self.modspec["add_file_common_args"]:
|
||||||
|
file_args = self.module.load_file_common_arguments(self.params, path=dest)
|
||||||
|
diff = TypedDiff()
|
||||||
|
self.changed |= self.module.set_fs_attributes_if_different(file_args, diff=diff)
|
||||||
|
self.diff(diff)
|
||||||
|
|
||||||
|
|
||||||
class SystemdUnitModule(AnsibleModule):
|
class SystemdUnitModule(AnsibleModule):
|
||||||
#: path of the unitfile managed by this module
|
#: path of the unitfile managed by this module
|
||||||
|
@ -286,34 +347,6 @@ class SystemdUnitModule(AnsibleModule):
|
||||||
path = self.tmpdir / "newunit"
|
path = self.tmpdir / "newunit"
|
||||||
with open(path, "w") as unit:
|
with open(path, "w") as unit:
|
||||||
unit.write(self.unit())
|
unit.write(self.unit())
|
||||||
self.module.set_owner_if_different(path.as_posix(), "root", False)
|
|
||||||
self.module.set_group_if_different(path.as_posix(), "root", False)
|
|
||||||
self.module.set_mode_if_different(path.as_posix(), "0644", False)
|
|
||||||
if self.unitfile.exists():
|
|
||||||
diff = TypedDiff() # type:ignore[reportCallIssue]
|
|
||||||
self.changed = self.changed | self.module.set_owner_if_different(
|
|
||||||
self.unitfile.as_posix(),
|
|
||||||
"root",
|
|
||||||
self.result["changed"],
|
|
||||||
diff,
|
|
||||||
)
|
|
||||||
self.diff(diff)
|
|
||||||
diff = TypedDiff() # type:ignore[reportCallIssue]
|
|
||||||
self.changed = self.changed | self.module.set_group_if_different(
|
|
||||||
self.unitfile.as_posix(),
|
|
||||||
"root",
|
|
||||||
self.result["changed"],
|
|
||||||
diff,
|
|
||||||
)
|
|
||||||
self.diff(diff)
|
|
||||||
diff = TypedDiff() # type:ignore[reportCallIssue]
|
|
||||||
self.changed = self.changed | self.module.set_mode_if_different(
|
|
||||||
self.unitfile.as_posix(),
|
|
||||||
"0644",
|
|
||||||
self.result["changed"],
|
|
||||||
diff,
|
|
||||||
)
|
|
||||||
self.diff(diff)
|
|
||||||
|
|
||||||
def check(self): # pragma: nocover
|
def check(self): # pragma: nocover
|
||||||
self.set("unitfile", self.unitfile.as_posix())
|
self.set("unitfile", self.unitfile.as_posix())
|
||||||
|
@ -334,10 +367,7 @@ class SystemdUnitModule(AnsibleModule):
|
||||||
self.check()
|
self.check()
|
||||||
if not self.changed:
|
if not self.changed:
|
||||||
return
|
return
|
||||||
self.module.atomic_move(
|
self.move_file(self.tmpdir / "newunit", self.unitfile)
|
||||||
src=(self.tmpdir / "newunit").as_posix(),
|
|
||||||
dest=self.unitfile.as_posix(),
|
|
||||||
)
|
|
||||||
if hasattr(self, "post") and self.post is not None:
|
if hasattr(self, "post") and self.post is not None:
|
||||||
self.post()
|
self.post()
|
||||||
|
|
||||||
|
|
|
@ -7,22 +7,18 @@ name = "ansible-module"
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
description = 'Helps with developing modules for ansible in an easier manner'
|
description = 'Helps with developing modules for ansible in an easier manner'
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.7"
|
requires-python = ">=3.11"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
keywords = []
|
keywords = []
|
||||||
authors = [{ name = "Sebastian Tobie", email = "sebastian@sebastian-tobie.de" }]
|
authors = [{ name = "Sebastian Tobie", email = "sebastian@sebastian-tobie.de" }]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 4 - Beta",
|
"Development Status :: 4 - Beta",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
]
|
]
|
||||||
dependencies = []
|
dependencies = ["ansible>=11.3.0"]
|
||||||
[tool.hatch.build]
|
[tool.hatch.build]
|
||||||
directory = "dist/python"
|
directory = "dist/python"
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "0.4.4"
|
__version__ = "0.5.0"
|
||||||
|
|
|
@ -4,10 +4,15 @@ import pathlib
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from .module_utils.module import AnsibleModule
|
||||||
|
|
||||||
sys.path.append(".")
|
sys.path.append(".")
|
||||||
mindocstring = "DOCUMENTATION = ''''''"
|
mindocstring = "DOCUMENTATION = ''''''"
|
||||||
moduledir = pathlib.Path("plugins/modules")
|
moduledir = pathlib.Path("plugins/modules")
|
||||||
regex = re.compile("DOCUMENTATION *= *r?(?P<quote>\"{3}|'{3})(---)?.*?(?P=quote)", re.MULTILINE | re.DOTALL)
|
regex = re.compile(
|
||||||
|
"(?P<type>DOCUMENTATION|RETURNS) *= *r?(?P<quote>\"{3}|'{3})(---)?.*?(?P=quote)",
|
||||||
|
re.MULTILINE | re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
@ -20,6 +25,7 @@ def main() -> None:
|
||||||
if modfile.name in ("__init__.py", "__pycache__", "unit.py.example"):
|
if modfile.name in ("__init__.py", "__pycache__", "unit.py.example"):
|
||||||
continue
|
continue
|
||||||
mod = importlib.import_module(".".join((modfile.parts[:-1]) + (modfile.stem,)))
|
mod = importlib.import_module(".".join((modfile.parts[:-1]) + (modfile.stem,)))
|
||||||
|
module: AnsibleModule
|
||||||
if hasattr(mod, "Module"):
|
if hasattr(mod, "Module"):
|
||||||
module = mod.Module
|
module = mod.Module
|
||||||
elif hasattr(mod, "__module_name__"):
|
elif hasattr(mod, "__module_name__"):
|
||||||
|
@ -29,6 +35,7 @@ def main() -> None:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
moddoc = module.doc()
|
moddoc = module.doc()
|
||||||
|
returns = module.returns()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print("Broken module. skipping {}".format(modfile))
|
print("Broken module. skipping {}".format(modfile))
|
||||||
continue
|
continue
|
||||||
|
@ -36,16 +43,37 @@ def main() -> None:
|
||||||
print("Error in documentation of module {}: {}".format(modfile, e))
|
print("Error in documentation of module {}: {}".format(modfile, e))
|
||||||
continue
|
continue
|
||||||
moddata = modfile.read_text()
|
moddata = modfile.read_text()
|
||||||
match = regex.search(moddata)
|
changed = False
|
||||||
if not match:
|
start = 0
|
||||||
|
while True:
|
||||||
|
match = regex.search(moddata, pos=start)
|
||||||
|
if not match:
|
||||||
|
break
|
||||||
|
changed = True
|
||||||
|
type = match.group("type")
|
||||||
|
content = ""
|
||||||
|
if type == "DOCUMENTATION":
|
||||||
|
content = moddoc
|
||||||
|
elif type == "RETURNS":
|
||||||
|
content = returns
|
||||||
|
else:
|
||||||
|
Exception("Please update the script: Unknown Type")
|
||||||
|
after = moddata[match.end() :]
|
||||||
|
moddata = "{pre}{type} = r{quote}{doc}{quote}".format(
|
||||||
|
pre=moddata[: match.start()],
|
||||||
|
type=type,
|
||||||
|
quote=match.group("quote"),
|
||||||
|
doc=content,
|
||||||
|
)
|
||||||
|
start = len(moddata)
|
||||||
|
moddata = f"{moddata}{after}"
|
||||||
|
if changed is False:
|
||||||
print(
|
print(
|
||||||
"no Documentation set for module {}. Please add at least \"{}\" to the file".format(
|
"no Documentation set for module {}. Please add at least \"{}\" to the file".format(
|
||||||
modfile.stem, mindocstring
|
modfile.stem, mindocstring
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
newmod = "{pre}DOCUMENTATION = {quote}{doc}{quote}{post}".format(
|
|
||||||
pre=moddata[: match.start()], quote=match.group("quote"), doc=moddoc, post=moddata[match.end() :]
|
modfile.write_text(moddata)
|
||||||
)
|
|
||||||
modfile.write_text(newmod)
|
|
||||||
print("updated the documentation of module {}".format(module.name))
|
print("updated the documentation of module {}".format(module.name))
|
||||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren