Matt Martz 2 weeks ago committed by GitHub
commit 14ebaa8d3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,2 @@
minor_changes:
- argument spec - Add strict type checkers that will not coerce for str, list, dict, bool, int, and float.

@ -8,6 +8,7 @@ import datetime
import os
from collections import deque
from functools import partial
from itertools import chain
from ansible.module_utils.common.collections import is_iterable
@ -107,11 +108,17 @@ PASS_BOOLS = ('check_mode', 'debug', 'diff', 'keep_remote_files', 'ignore_unknow
DEFAULT_TYPE_VALIDATORS = {
'str': check_type_str,
'str_strict': partial(check_type_str, allow_conversion=False),
'list': check_type_list,
'list_strict': partial(check_type_list, allow_conversion=False),
'dict': check_type_dict,
'dict_strict': partial(check_type_dict, allow_conversion=False),
'bool': check_type_bool,
'bool_strict': partial(check_type_bool, allow_conversion=False),
'int': check_type_int,
'int_strict': partial(check_type_int, allow_conversion=False),
'float': check_type_float,
'float_strict': partial(check_type_float, allow_conversion=False),
'path': check_type_path,
'raw': check_type_raw,
'jsonarg': check_type_jsonarg,
@ -613,6 +620,8 @@ def _validate_argument_types(argument_spec, parameters, prefix='', options_conte
continue
wanted_type = spec.get('type')
if wanted_type and spec.get('strict', False):
wanted_type = '%s_strict' % wanted_type
type_checker, wanted_name = _get_type_validator(wanted_type)
# Get param name for strings so we can later display this value in a useful error message if needed
# Only pass 'kwargs' to our checkers and ignore custom callable checkers
@ -627,6 +636,8 @@ def _validate_argument_types(argument_spec, parameters, prefix='', options_conte
try:
parameters[param] = type_checker(value, **kwargs)
elements_wanted_type = spec.get('elements', None)
if elements_wanted_type and spec.get('strict', False):
elements_wanted_type = '%s_strict' % elements_wanted_type
if elements_wanted_type:
elements = parameters[param]
if wanted_type != 'list' or not isinstance(elements, list):

@ -387,7 +387,7 @@ def check_type_str(value, allow_conversion=True, param=None, prefix=''):
raise TypeError(to_native(msg))
def check_type_list(value):
def check_type_list(value, allow_conversion=True):
"""Verify that the value is a list or convert to a list
A comma separated string will be split into a list. Raises a :class:`TypeError`
@ -401,6 +401,8 @@ def check_type_list(value):
"""
if isinstance(value, list):
return value
elif not allow_conversion:
raise TypeError('%s cannot be converted to a list' % type(value))
if isinstance(value, string_types):
return value.split(",")
@ -410,7 +412,7 @@ def check_type_list(value):
raise TypeError('%s cannot be converted to a list' % type(value))
def check_type_dict(value):
def check_type_dict(value, allow_conversion=True):
"""Verify that value is a dict or convert it to a dict and return it.
Raises :class:`TypeError` if unable to convert to a dict
@ -422,7 +424,7 @@ def check_type_dict(value):
if isinstance(value, dict):
return value
if isinstance(value, string_types):
if allow_conversion and isinstance(value, string_types):
if value.startswith("{"):
try:
return json.loads(value)
@ -464,7 +466,7 @@ def check_type_dict(value):
raise TypeError('%s cannot be converted to a dict' % type(value))
def check_type_bool(value):
def check_type_bool(value, allow_conversion=True):
"""Verify that the value is a bool or convert it to a bool and return it.
Raises :class:`TypeError` if unable to convert to a bool
@ -477,13 +479,13 @@ def check_type_bool(value):
if isinstance(value, bool):
return value
if isinstance(value, string_types) or isinstance(value, (int, float)):
if allow_conversion and isinstance(value, string_types) or isinstance(value, (int, float)):
return boolean(value)
raise TypeError('%s cannot be converted to a bool' % type(value))
def check_type_int(value):
def check_type_int(value, allow_conversion=True):
"""Verify that the value is an integer and return it or convert the value
to an integer and return it
@ -496,7 +498,7 @@ def check_type_int(value):
if isinstance(value, integer_types):
return value
if isinstance(value, string_types):
if allow_conversion and isinstance(value, string_types):
try:
return int(value)
except ValueError:
@ -505,7 +507,7 @@ def check_type_int(value):
raise TypeError('%s cannot be converted to an int' % type(value))
def check_type_float(value):
def check_type_float(value, allow_conversion=True):
"""Verify that value is a float or convert it to a float and return it
Raises :class:`TypeError` if unable to convert to a float
@ -517,7 +519,7 @@ def check_type_float(value):
if isinstance(value, float):
return value
if isinstance(value, (binary_type, text_type, int)):
if allow_conversion and isinstance(value, (binary_type, text_type, int)):
try:
return float(value)
except ValueError:
@ -526,11 +528,11 @@ def check_type_float(value):
raise TypeError('%s cannot be converted to a float' % type(value))
def check_type_path(value,):
def check_type_path(value):
"""Verify the provided value is a string or convert it to a string,
then return the expanded path
"""
value = check_type_str(value)
value = check_type_str(value, allow_conversion=False)
return os.path.expanduser(os.path.expandvars(value))

@ -31,6 +31,29 @@ def main():
'mapping': {
'type': 'dict',
},
'str_strict': {
'type': 'str',
'strict': True,
},
'list_strict': {
'type': 'list_strict',
},
'dict_strict': {
'type': 'dict',
'strict': True,
},
'bool_strict': {
'type': 'bool',
'strict': True,
},
'int_strict': {
'type': 'int',
'strict': True,
},
'float_strict': {
'type': 'float',
'strict': True,
},
'required_one_of': {
'required_one_of': [['thing', 'other']],
'type': 'list',

@ -490,6 +490,81 @@
other_alias: bar
register: alias_warning_listdict
- argspec:
str_strict: 'one'
required: value
required_one_of_one: value
- argspec:
str_strict: 1
required: value
required_one_of_one: value
ignore_errors: true
register: str_strict_fail
- argspec:
list_strict:
- one
- two
required: value
required_one_of_one: value
- argspec:
list_strict: one,two
required: value
required_one_of_one: value
ignore_errors: true
register: list_strict_fail
- argspec:
dict_strict:
one: two
required: value
required_one_of_one: value
- argspec:
dict_strict: '{"one": "two"}'
required: value
required_one_of_one: value
ignore_errors: true
register: dict_strict_fail
- argspec:
bool_strict: true
required: value
required_one_of_one: value
- argspec:
bool_strict: 'true'
required: value
required_one_of_one: value
ignore_errors: true
register: bool_strict_fail
- argspec:
int_strict: 1
required: value
required_one_of_one: value
- argspec:
int_strict: '1'
required: value
required_one_of_one: value
ignore_errors: true
register: int_strict_fail
- argspec:
float_strict: 1.1
required: value
required_one_of_one: value
- argspec:
float_strict: 1
required: value
required_one_of_one: value
ignore_errors: true
register: float_strict_fail
- assert:
that:
- argspec_required_fail is failed
@ -657,3 +732,10 @@
- "'Both option apply_defaults.bar and its alias apply_defaults.bar_alias2 are set.' in alias_warning_dict.warnings"
- "'Both option required_one_of[0].other and its alias required_one_of[0].other_alias are set.' in alias_warning_listdict.warnings"
- str_strict_fail is failed
- list_strict_fail is failed
- dict_strict_fail is failed
- bool_strict_fail is failed
- int_strict_fail is failed
- float_strict_fail is failed

@ -501,7 +501,7 @@
- name: test check mode creates/removes message
command:
cmd: "true"
creates: yes
creates: "yes"
check_mode: yes
register: result

@ -203,7 +203,7 @@ seealso_schema = Schema(
argument_spec_types = ['bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw',
'sid', 'str']
'sid', 'str', 'str_strict', 'list_strict', 'dict_strict', 'bool_strict', 'int_strict', 'float_strict']
argument_spec_modifiers = {
@ -291,6 +291,7 @@ def argument_spec_schema(for_collection):
any_string_types: {
'type': Any(is_callable, *argument_spec_types),
'elements': Any(*argument_spec_types),
'strict': bool,
'default': object,
'fallback': Any(
(is_callable, list_string_types),

Loading…
Cancel
Save