Display parameter name in string conversion warning (#57145)

Add prefix to error message for nested options. This is helpful if a subelement key has the same name as a top level key.
This commit is contained in:
Sam Doran 2020-06-09 13:05:53 -04:00 committed by GitHub
parent a64418c2b3
commit 6065638e00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 7 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- add parameter name to warning message when values are converted to strings (https://github.com/ansible/ansible/pull/57145)

View file

@ -1613,7 +1613,7 @@ class AnsibleModule(object):
def safe_eval(self, value, locals=None, include_exceptions=False): def safe_eval(self, value, locals=None, include_exceptions=False):
return safe_eval(value, locals, include_exceptions) return safe_eval(value, locals, include_exceptions)
def _check_type_str(self, value): def _check_type_str(self, value, param=None, prefix=''):
opts = { opts = {
'error': False, 'error': False,
'warn': False, 'warn': False,
@ -1626,12 +1626,22 @@ class AnsibleModule(object):
return check_type_str(value, allow_conversion) return check_type_str(value, allow_conversion)
except TypeError: except TypeError:
common_msg = 'quote the entire value to ensure it does not change.' common_msg = 'quote the entire value to ensure it does not change.'
from_msg = '{0!r}'.format(value)
to_msg = '{0!r}'.format(to_text(value))
if param is not None:
if prefix:
param = '{0}{1}'.format(prefix, param)
from_msg = '{0}: {1!r}'.format(param, value)
to_msg = '{0}: {1!r}'.format(param, to_text(value))
if self._string_conversion_action == 'error': if self._string_conversion_action == 'error':
msg = common_msg.capitalize() msg = common_msg.capitalize()
raise TypeError(to_native(msg)) raise TypeError(to_native(msg))
elif self._string_conversion_action == 'warn': elif self._string_conversion_action == 'warn':
msg = ('The value {0!r} (type {0.__class__.__name__}) in a string field was converted to {1!r} (type string). ' msg = ('The value "{0}" (type {1.__class__.__name__}) was converted to "{2}" (type string). '
'If this does not look like what you expect, {2}').format(value, to_text(value), common_msg) 'If this does not look like what you expect, {3}').format(from_msg, value, to_msg, common_msg)
self.warn(to_native(msg)) self.warn(to_native(msg))
return to_native(value, errors='surrogate_or_strict') return to_native(value, errors='surrogate_or_strict')
@ -1716,7 +1726,7 @@ class AnsibleModule(object):
if not self.bypass_checks: if not self.bypass_checks:
self._check_required_arguments(spec, param) self._check_required_arguments(spec, param)
self._check_argument_types(spec, param) self._check_argument_types(spec, param, new_prefix)
self._check_argument_values(spec, param) self._check_argument_values(spec, param)
self._check_required_together(v.get('required_together', None), param) self._check_required_together(v.get('required_together', None), param)
@ -1751,9 +1761,16 @@ class AnsibleModule(object):
def _handle_elements(self, wanted, param, values): def _handle_elements(self, wanted, param, values):
type_checker, wanted_name = self._get_wanted_type(wanted, param) type_checker, wanted_name = self._get_wanted_type(wanted, param)
validated_params = [] validated_params = []
# Get param name for strings so we can later display this value in a useful error message if needed
kwargs = {}
if wanted_name == 'str':
if isinstance(param, string_types):
kwargs['param'] = param
elif isinstance(param, dict):
kwargs['param'] = list(param.keys())[0]
for value in values: for value in values:
try: try:
validated_params.append(type_checker(value)) validated_params.append(type_checker(value, **kwargs))
except (TypeError, ValueError) as e: except (TypeError, ValueError) as e:
msg = "Elements value for option %s" % param msg = "Elements value for option %s" % param
if self._options_context: if self._options_context:
@ -1762,7 +1779,7 @@ class AnsibleModule(object):
self.fail_json(msg=msg) self.fail_json(msg=msg)
return validated_params return validated_params
def _check_argument_types(self, spec=None, param=None): def _check_argument_types(self, spec=None, param=None, prefix=''):
''' ensure all arguments have the requested type ''' ''' ensure all arguments have the requested type '''
if spec is None: if spec is None:
@ -1780,8 +1797,17 @@ class AnsibleModule(object):
continue continue
type_checker, wanted_name = self._get_wanted_type(wanted, k) type_checker, wanted_name = self._get_wanted_type(wanted, k)
# Get param name for strings so we can later display this value in a useful error message if needed
kwargs = {}
if wanted_name == 'str':
kwargs['param'] = list(param.keys())[0]
# Get the name of the parent key if this is a nested option
if prefix:
kwargs['prefix'] = prefix
try: try:
param[k] = type_checker(value) param[k] = type_checker(value, **kwargs)
wanted_elements = v.get('elements', None) wanted_elements = v.get('elements', None)
if wanted_elements: if wanted_elements:
if wanted != 'list' or not isinstance(param[k], list): if wanted != 'list' or not isinstance(param[k], list):