diff --git a/compose/config/validation.py b/compose/config/validation.py index 632bdf03b..44763fda3 100644 --- a/compose/config/validation.py +++ b/compose/config/validation.py @@ -117,6 +117,38 @@ def process_errors(errors, service_name=None): else: return str(schema['type']) + def _parse_valid_types_from_validator(validator): + """ + A validator value can be either an array of valid types or a string of + a valid type. Parse the valid types and prefix with the correct article. + """ + pre_msg_type_prefix = "a" + last_msg_type_prefix = "a" + types_requiring_an = ["array", "object"] + + if isinstance(validator, list): + last_type = validator.pop() + types_from_validator = ", ".join(validator) + + if validator[0] in types_requiring_an: + pre_msg_type_prefix = "an" + + if last_type in types_requiring_an: + last_msg_type_prefix = "an" + + msg = "{} {} or {} {}".format( + pre_msg_type_prefix, + types_from_validator, + last_msg_type_prefix, + last_type + ) + else: + if validator in types_requiring_an: + pre_msg_type_prefix = "an" + msg = "{} {}".format(pre_msg_type_prefix, validator) + + return msg + root_msgs = [] invalid_keys = [] required = [] @@ -176,19 +208,16 @@ def process_errors(errors, service_name=None): service_name, config_key, valid_type_msg) ) elif error.validator == 'type': - msg = "a" - if error.validator_value == "array": - msg = "an" + msg = _parse_valid_types_from_validator(error.validator_value) if len(error.path) > 0: config_key = " ".join(["'%s'" % k for k in error.path]) type_errors.append( "Service '{}' configuration key {} contains an invalid " - "type, it should be {} {}".format( + "type, it should be {}".format( service_name, config_key, - msg, - error.validator_value)) + msg)) else: root_msgs.append( "Service '{}' doesn\'t have any configuration options. " diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py index 870adcf81..90d7a6a26 100644 --- a/tests/unit/config_test.py +++ b/tests/unit/config_test.py @@ -269,6 +269,21 @@ class ConfigTest(unittest.TestCase): ) self.assertEqual(service[0]['entrypoint'], entrypoint) + def test_validation_message_for_invalid_type_when_multiple_types_allowed(self): + expected_error_msg = "Service 'web' configuration key 'mem_limit' contains an invalid type, it should be a number or a string" + + with self.assertRaisesRegexp(ConfigurationError, expected_error_msg): + config.load( + config.ConfigDetails( + {'web': { + 'image': 'busybox', + 'mem_limit': ['incorrect'] + }}, + 'working_dir', + 'filename.yml' + ) + ) + class InterpolationTest(unittest.TestCase): @mock.patch.dict(os.environ)