Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/69901.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed `salt.utils.functools` `namespaced_function`/`alias_function` dropping keyword-only argument defaults in copied function
1 change: 1 addition & 0 deletions changelog/69906.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed handling of keyword-only parameters and positional-only parameters when calling module functions, most notably from the CLI, in state application and via the mine or `module.run`.
1 change: 1 addition & 0 deletions changelog/69919.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed `module.run` handling of positional arguments to parameters that have default values
2 changes: 1 addition & 1 deletion salt/fileserver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ def envs(self, back=None, sources=False):
fstr = f"{fsb}.envs"
kwargs = (
{"ignore_cache": True}
if "ignore_cache" in _argspec(self.servers[fstr]).args
if "ignore_cache" in _argspec(self.servers[fstr]).namedargs
and self.opts["__role"] == "minion"
else {}
)
Expand Down
9 changes: 1 addition & 8 deletions salt/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"""

import fnmatch
import itertools
import logging
import os
import shutil
Expand Down Expand Up @@ -146,14 +145,8 @@ def _get_args_kwargs(self, fun, args=None):
if args is None:
args = []
if argspec.args:
# Iterate in reverse order to ensure we get the correct default
# value for the positional argument.
for arg, default in itertools.zip_longest(
reversed(argspec.args), reversed(argspec.defaults or ())
):
for arg, default in argspec.argdefaults.items():
args.append(self.opts.get(arg, default))
# Reverse the args so that they are in the correct order
args = args[::-1]

if argspec.keywords is None:
kwargs = {}
Expand Down
4 changes: 2 additions & 2 deletions salt/master.py
Original file line number Diff line number Diff line change
Expand Up @@ -2494,7 +2494,7 @@ def _prep_pub(self, minions, jid, clear_load, extra, missing):

# Check if 'minions' is included in returner's save_load arg_spec.
# This may be missing in custom returners, which we should warn about.
if "minions" not in arg_spec.args:
if "minions" not in arg_spec.namedargs:
log.critical(
"The specified returner used for the external job cache "
"'%s' does not have a 'minions' kwarg in the returner's "
Expand Down Expand Up @@ -2522,7 +2522,7 @@ def _prep_pub(self, minions, jid, clear_load, extra, missing):
# always write out to the master job caches
try:
fstr = "{}.save_load".format(self.opts["master_job_cache"])
self.mminion.returners[fstr](clear_load["jid"], clear_load, minions)
self.mminion.returners[fstr](clear_load["jid"], clear_load, minions=minions)
except KeyError:
log.critical(
"The specified returner used for the master job cache "
Expand Down
37 changes: 22 additions & 15 deletions salt/minion.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,30 +393,37 @@ def load_args_and_kwargs(func, args, data=None, ignore_invalid=False):
# Skip __kwarg__ when checking kwargs
if key == "__kwarg__":
continue
if argspec.keywords or key in argspec.args:
# Function supports **kwargs or is a positional argument to
# the function.
if (
argspec.keywords
or key in argspec.kwonlyargs
or (key in argspec.args and key not in argspec.posonlyargs)
):
# Function supports **kwargs or has a parameter with
# this name that can be passed a keyword argument.
_kwargs[key] = val
else:
# **kwargs not in argspec and parsed argument name not in
# list of positional arguments. This keyword argument is
# invalid.
# **kwargs not in argspec and parsed argument name not
# a parameter that can be passed a keyword argument.
# This keyword argument is invalid.
invalid_kwargs.append(f"{key}={val}")
continue

else:
string_kwarg = salt.utils.args.parse_input([arg], condition=False)[
1
] # pylint: disable=W0632
string_kwarg = salt.utils.args.parse_input([arg], condition=False)[1]
if string_kwarg:
if argspec.keywords or next(iter(string_kwarg.keys())) in argspec.args:
# Function supports **kwargs or is a positional argument to
# the function.
key = next(iter(string_kwarg))
if (
argspec.keywords
or key in argspec.kwonlyargs
or (key in argspec.args and key not in argspec.posonlyargs)
):
# Function supports **kwargs or has a parameter with
# this name that can be passed a keyword argument.
_kwargs.update(string_kwarg)
else:
# **kwargs not in argspec and parsed argument name not in
# list of positional arguments. This keyword argument is
# invalid.
# **kwargs not in argspec and parsed argument name not
# a parameter that can be passed a keyword argument.
# This keyword argument is invalid.
for key, val in string_kwarg.items():
invalid_kwargs.append(f"{key}={val}")
else:
Expand Down
4 changes: 2 additions & 2 deletions salt/modules/saltutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -2009,7 +2009,7 @@ def runner(

if name in rclient.functions:
aspec = salt.utils.args.get_function_argspec(rclient.functions[name])
if "saltenv" in aspec.args:
if "saltenv" in aspec.namedargs:
kwarg["saltenv"] = saltenv

if name in ["state.orchestrate", "state.orch", "state.sls"]:
Expand Down Expand Up @@ -2095,7 +2095,7 @@ def wheel(name, *args, **kwargs):
try:
if name in wheel_client.functions:
aspec = salt.utils.args.get_function_argspec(wheel_client.functions[name])
if "saltenv" in aspec.args:
if "saltenv" in aspec.namedargs:
valid_kwargs["saltenv"] = saltenv

if jid:
Expand Down
10 changes: 6 additions & 4 deletions salt/pillar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1147,10 +1147,12 @@ def _external_pillar_data(self, pillar, val, key):
Builds actual pillar data structure and updates the ``pillar`` variable
"""
ext = None
args = salt.utils.args.get_function_argspec(self.ext_pillars[key]).args
valid_kwargs = salt.utils.args.get_function_argspec(
self.ext_pillars[key]
).namedargs

if isinstance(val, dict):
if ("extra_minion_data" in args) and self.extra_minion_data:
if ("extra_minion_data" in valid_kwargs) and self.extra_minion_data:
ext = self.ext_pillars[key](
self.minion_id,
pillar,
Expand All @@ -1160,7 +1162,7 @@ def _external_pillar_data(self, pillar, val, key):
else:
ext = self.ext_pillars[key](self.minion_id, pillar, **val)
elif isinstance(val, list):
if ("extra_minion_data" in args) and self.extra_minion_data:
if ("extra_minion_data" in valid_kwargs) and self.extra_minion_data:
ext = self.ext_pillars[key](
self.minion_id,
pillar,
Expand All @@ -1170,7 +1172,7 @@ def _external_pillar_data(self, pillar, val, key):
else:
ext = self.ext_pillars[key](self.minion_id, pillar, *val)
else:
if ("extra_minion_data" in args) and self.extra_minion_data:
if ("extra_minion_data" in valid_kwargs) and self.extra_minion_data:
ext = self.ext_pillars[key](
self.minion_id,
pillar,
Expand Down
16 changes: 3 additions & 13 deletions salt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1498,19 +1498,9 @@ def verify_data(self, data):
else:
# First verify that the parameters are met
aspec = salt.utils.args.get_function_argspec(self.states[full])
arglen = 0
deflen = 0
if isinstance(aspec.args, list):
arglen = len(aspec.args)
if isinstance(aspec.defaults, tuple):
deflen = len(aspec.defaults)
for ind in range(arglen - deflen):
if aspec.args[ind] not in data:
errors.append(
"Missing parameter {} for state {}".format(
aspec.args[ind], full
)
)
for req in aspec.allreq:
if req not in data:
errors.append(f"Missing parameter {req} for state {full}")
# If this chunk has a recursive require, then it will cause a
# recursive loop when executing, check for it
reqdec = ""
Expand Down
4 changes: 2 additions & 2 deletions salt/states/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ def _get_systemd_only(func, kwargs):

ret = {}
warnings = []
valid_args = _argspec(func).args
valid_kwargs = _argspec(func).namedargs
for systemd_arg in SYSTEMD_ONLY:
if systemd_arg in kwargs and systemd_arg in valid_args:
if systemd_arg in kwargs and systemd_arg in valid_kwargs:
if _get_systemd_only.HAS_SYSTEMD:
ret[systemd_arg] = kwargs[systemd_arg]
else:
Expand Down
Loading
Loading