diff --git a/heatclient/common/environment_format.py b/heatclient/common/environment_format.py index 0b876b6b..5c29aad0 100644 --- a/heatclient/common/environment_format.py +++ b/heatclient/common/environment_format.py @@ -21,15 +21,21 @@ SECTIONS = (PARAMETER_DEFAULTS, PARAMETERS, RESOURCE_REGISTRY) = \ def parse(env_str): - '''Takes a string and returns a dict containing the parsed structure. + """Takes a string and returns a dict containing the parsed structure. This includes determination of whether the string is using the YAML format. - ''' + """ try: env = yaml.load(env_str, Loader=template_format.yaml_loader) - except yaml.YAMLError as yea: - raise ValueError(yea) + except yaml.YAMLError: + # NOTE(prazumovsky): we need to return more informative error for + # user, so use SafeLoader, which return error message with template + # snippet where error has been occurred. + try: + env = yaml.load(env_str, Loader=yaml.SafeLoader) + except yaml.YAMLError as yea: + raise ValueError(yea) else: if env is None: env = {} diff --git a/heatclient/common/template_format.py b/heatclient/common/template_format.py index 2f4a91ac..1636ebb7 100644 --- a/heatclient/common/template_format.py +++ b/heatclient/common/template_format.py @@ -40,18 +40,24 @@ yaml_loader.add_constructor(u'tag:yaml.org,2002:timestamp', def parse(tmpl_str): - '''Takes a string and returns a dict containing the parsed structure. + """Takes a string and returns a dict containing the parsed structure. This includes determination of whether the string is using the JSON or YAML format. - ''' + """ if tmpl_str.startswith('{'): tpl = json.loads(tmpl_str) else: try: tpl = yaml.load(tmpl_str, Loader=yaml_loader) - except yaml.YAMLError as yea: - raise ValueError(yea) + except yaml.YAMLError: + # NOTE(prazumovsky): we need to return more informative error for + # user, so use SafeLoader, which return error message with template + # snippet where error has been occurred. + try: + tpl = yaml.load(tmpl_str, Loader=yaml.SafeLoader) + except yaml.YAMLError as yea: + raise ValueError(yea) else: if tpl is None: tpl = {} diff --git a/heatclient/tests/unit/test_environment_format.py b/heatclient/tests/unit/test_environment_format.py index 702619dd..faea505d 100644 --- a/heatclient/tests/unit/test_environment_format.py +++ b/heatclient/tests/unit/test_environment_format.py @@ -13,6 +13,7 @@ from heatclient.common import environment_format import mock +import six import testscenarios import testtools import yaml @@ -78,3 +79,14 @@ class YamlParseExceptions(testtools.TestCase): self.assertRaises(ValueError, environment_format.parse, text) + + +class DetailedYAMLParseExceptions(testtools.TestCase): + + def test_parse_to_value_exception(self): + yaml = """not important +but very: + - incorrect +""" + ex = self.assertRaises(ValueError, environment_format.parse, yaml) + self.assertIn('but very:\n ^', six.text_type(ex)) diff --git a/heatclient/tests/unit/test_template_format.py b/heatclient/tests/unit/test_template_format.py index ca8d1fc2..ffc1ec4f 100644 --- a/heatclient/tests/unit/test_template_format.py +++ b/heatclient/tests/unit/test_template_format.py @@ -11,6 +11,7 @@ # under the License. import mock +import six import testscenarios import testtools import yaml @@ -48,3 +49,14 @@ Resources: {} Outputs: {} ''' self.assertRaises(ValueError, template_format.parse, yaml2) + + +class DetailedYAMLParseExceptions(testtools.TestCase): + + def test_parse_to_value_exception(self): + yaml = """not important +but very: + - incorrect +""" + ex = self.assertRaises(ValueError, template_format.parse, yaml) + self.assertIn('but very:\n ^', six.text_type(ex))