Mini Shell
"""
Utility functions to modify other functions
"""
import logging
import types
import salt.utils.args
import salt.utils.versions
from salt.exceptions import SaltInvocationError
log = logging.getLogger(__name__)
def namespaced_function(function, global_dict, defaults=None, preserve_context=None):
"""
Redefine (clone) a function under a different globals() namespace scope.
Any keys missing in the passed ``global_dict`` that is present in the
passed function ``__globals__`` attribute get's copied over into
``global_dict``, thus avoiding ``NameError`` from modules imported in
the original function module.
:param defaults:
.. deprecated:: 3005
:param preserve_context:
.. deprecated:: 3005
Allow keeping the context taken from orignal namespace,
and extend it with globals() taken from
new targetted namespace.
"""
if defaults is not None:
salt.utils.versions.warn_until(
3008,
"Passing 'defaults' to 'namespaced_function' is deprecated, slated "
"for removal in {version} and no longer does anything for the "
"function being namespaced.",
)
if preserve_context is not None:
salt.utils.versions.warn_until(
3008,
"Passing 'preserve_context' to 'namespaced_function' is deprecated, "
"slated for removal in {version} and no longer does anything for the "
"function being namespaced.",
)
# Make sure that any key on the globals of the function being copied get's
# added to the destination globals dictionary, if not present.
for key, value in function.__globals__.items():
if key not in global_dict:
global_dict[key] = value
new_namespaced_function = types.FunctionType(
function.__code__,
global_dict,
name=function.__name__,
argdefs=function.__defaults__,
closure=function.__closure__,
)
new_namespaced_function.__dict__.update(function.__dict__)
return new_namespaced_function
def alias_function(fun, name, doc=None):
"""
Copy a function
"""
alias_fun = types.FunctionType(
fun.__code__,
fun.__globals__,
str(name),
fun.__defaults__,
fun.__closure__,
)
alias_fun.__dict__.update(fun.__dict__)
if doc and isinstance(doc, str):
alias_fun.__doc__ = doc
else:
orig_name = fun.__name__
alias_msg = f"\nThis function is an alias of ``{orig_name}``.\n"
alias_fun.__doc__ = alias_msg + (fun.__doc__ or "")
return alias_fun
def parse_function(function_arguments):
"""
Helper function to parse function_arguments (module.run format)
into args and kwargs.
This function is similar to salt.utils.data.repack_dictlist, except that this
handles mixed (i.e. dict and non-dict) arguments in the input list.
:param list function_arguments: List of items and dicts with kwargs.
:rtype: dict
:return: Dictionary with ``args`` and ``kwargs`` keyword.
"""
function_args = []
function_kwargs = {}
for item in function_arguments:
if isinstance(item, dict):
function_kwargs.update(item)
else:
function_args.append(item)
return {"args": function_args, "kwargs": function_kwargs}
def call_function(salt_function, *args, **kwargs):
"""
Calls a function from the specified module.
:param function salt_function: Function reference to call
:return: The result of the function call
"""
argspec = salt.utils.args.get_function_argspec(salt_function)
# function_kwargs is initialized to a dictionary of keyword arguments the function to be run accepts
function_kwargs = dict(
zip(
argspec.args[-len(argspec.defaults or []) :],
argspec.defaults or [],
)
)
# expected_args is initialized to a list of positional arguments that the function to be run accepts
expected_args = argspec.args[
: len(argspec.args or []) - len(argspec.defaults or [])
]
function_args, kw_to_arg_type = [], {}
for funcset in reversed(args or []):
if not isinstance(funcset, dict):
# We are just receiving a list of args to the function to be run, so just append
# those to the arg list that we will pass to the func.
function_args.append(funcset)
else:
for kwarg_key in funcset.keys():
# We are going to pass in a keyword argument. The trick here is to make certain
# that if we find that in the *args* list that we pass it there and not as a kwarg
if kwarg_key in expected_args:
kw_to_arg_type[kwarg_key] = funcset[kwarg_key]
else:
# Otherwise, we're good and just go ahead and pass the keyword/value pair into
# the kwargs list to be run.
function_kwargs.update(funcset)
function_args.reverse()
# Add kwargs passed as kwargs :)
function_kwargs.update(kwargs)
for arg in expected_args:
if arg in kw_to_arg_type:
function_args.append(kw_to_arg_type[arg])
_exp_prm = len(argspec.args or []) - len(argspec.defaults or [])
_passed_prm = len(function_args)
missing = []
if _exp_prm > _passed_prm:
for arg in argspec.args[_passed_prm:]:
if arg not in function_kwargs:
missing.append(arg)
else:
# Found the expected argument as a keyword
# increase the _passed_prm count
_passed_prm += 1
if missing:
raise SaltInvocationError("Missing arguments: {}".format(", ".join(missing)))
elif _exp_prm > _passed_prm:
raise SaltInvocationError(
"Function expects {} positional parameters, got only {}".format(
_exp_prm, _passed_prm
)
)
return salt_function(*function_args, **function_kwargs)
Zerion Mini Shell 1.0