Skip to content

Commit eb7868d

Browse files
authored
Merge pull request #7808 from hssyoo/revert-7787-change-cfn-yml-spec
Revert "Use YAML 1.1 spec for CFN"
2 parents 23c3192 + 8fe631a commit eb7868d

File tree

5 files changed

+27
-49
lines changed

5 files changed

+27
-49
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "bugfix",
3+
"category": "cloudformation",
4+
"description": "Reverts aws/aws-cli`#7787 <https://github.com/aws/aws-cli/issues/7787>`__ and the associated regression aws/aws-cli`#7805 <https://github.com/aws/aws-cli/issues/7805>`__"
5+
}

awscli/customizations/cloudformation/yamlhelper.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,21 @@ def _dict_representer(dumper, data):
6363
return dumper.represent_dict(data.items())
6464

6565

66-
def _str_representer(dumper, data):
67-
# ruamel removes quotes from values that are unambiguously strings.
68-
# Values like 0888888 can only be a string because integers can't
69-
# have leading 0s and octals can't have the digits 8 and 9.
70-
# However, CloudFormation treats these nonoctal values as integers
71-
# and removes the leading 0s. This logic ensures that nonoctal
72-
# values are quoted when dumped.
73-
style = None
74-
if re.match('^0[0-9]*[89][0-9]*$', data):
75-
style = "'"
76-
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style=style)
66+
def _add_yaml_1_1_boolean_resolvers(resolver_cls):
67+
# CloudFormation treats unquoted values that are YAML 1.1 native
68+
# booleans as booleans, rather than strings. In YAML 1.2, the only
69+
# boolean values are "true" and "false" so values such as "yes" and "no"
70+
# when loaded as strings are not quoted when dumped. This logic ensures
71+
# that we dump these values with quotes so that CloudFormation treats
72+
# these values as strings and not booleans.
73+
boolean_regex = re.compile(
74+
'^(?:yes|Yes|YES|no|No|NO'
75+
'|true|True|TRUE|false|False|FALSE'
76+
'|on|On|ON|off|Off|OFF)$'
77+
)
78+
boolean_first_chars = list(u'yYnNtTfFoO')
79+
resolver_cls.add_implicit_resolver(
80+
'tag:yaml.org,2002:bool', boolean_regex, boolean_first_chars)
7781

7882

7983
def yaml_dump(dict_to_dump):
@@ -84,11 +88,10 @@ def yaml_dump(dict_to_dump):
8488
"""
8589

8690
yaml = ruamel.yaml.YAML(typ="safe", pure=True)
87-
yaml.version = (1, 1)
8891
yaml.default_flow_style = False
8992
yaml.Representer = FlattenAliasRepresenter
93+
_add_yaml_1_1_boolean_resolvers(yaml.Resolver)
9094
yaml.Representer.add_representer(OrderedDict, _dict_representer)
91-
yaml.Representer.add_representer(str, _str_representer)
9295

9396
return dump_yaml_to_str(yaml, dict_to_dump)
9497

@@ -108,12 +111,12 @@ def yaml_parse(yamlstr):
108111
return json.loads(yamlstr, object_pairs_hook=OrderedDict)
109112
except ValueError:
110113
yaml = ruamel.yaml.YAML(typ="safe", pure=True)
111-
yaml.version = (1, 1)
112114
yaml.Constructor.add_constructor(
113115
ruamel.yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
114116
_dict_constructor)
115117
yaml.Constructor.add_multi_constructor(
116118
"!", intrinsics_multi_constructor)
119+
_add_yaml_1_1_boolean_resolvers(yaml.Resolver)
117120

118121
return yaml.load(yamlstr)
119122

awscli/testutils.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
from pprint import pformat
3535
from subprocess import Popen, PIPE
3636
import unittest
37-
import re
3837

3938
from awscli.compat import StringIO
4039

@@ -59,7 +58,6 @@
5958
import awscli.clidriver
6059
from awscli.plugin import load_plugins
6160
from awscli.clidriver import CLIDriver
62-
from awscli.customizations.cloudformation.yamlhelper import yaml_dump
6361

6462

6563
_LOADER = botocore.loaders.Loader()
@@ -1014,8 +1012,3 @@ def wait(self, check, *args, **kwargs):
10141012
def _fail_message(self, attempts, successes):
10151013
format_args = (attempts, successes)
10161014
return 'Failed after %s attempts, only had %s successes' % format_args
1017-
1018-
1019-
def yaml_dump_without_header(dict_to_dump):
1020-
dumped_yaml = yaml_dump(dict_to_dump)
1021-
return re.sub('%YAML 1.1\n---(\n| )', '', dumped_yaml)

tests/functional/cloudformation/test_package.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from awscli.customizations.cloudformation.artifact_exporter import make_zip
2424
from awscli.customizations.cloudformation.yamlhelper import yaml_dump
2525
from awscli.customizations.cloudformation.artifact_exporter import Template
26-
from awscli.testutils import yaml_dump_without_header, skip_if_windows
26+
from awscli.testutils import skip_if_windows
2727

2828

2929
class TestPackageZipFiles(TestCase):
@@ -87,7 +87,7 @@ def _generate_template_cases():
8787
def test_known_templates(input_template, output_template):
8888
template = Template(input_template, os.getcwd(), None)
8989
exported = template.export()
90-
result = yaml_dump_without_header(exported)
90+
result = yaml_dump(exported)
9191
expected = open(output_template, 'r').read()
9292

9393
assert result == expected, (

tests/unit/customizations/cloudformation/test_yamlhelper.py

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from awscli.customizations.cloudformation.deployer import Deployer
2121
from awscli.customizations.cloudformation.yamlhelper import yaml_parse, yaml_dump
2222
from tests.unit.customizations.cloudformation import BaseYAMLTest
23-
from awscli.testutils import yaml_dump_without_header
2423

2524

2625
class TestYaml(BaseYAMLTest):
@@ -146,7 +145,7 @@ def test_parse_yaml_preserve_elements_order(self):
146145
])
147146
self.assertEqual(expected_dict, output_dict)
148147

149-
output_template = yaml_dump_without_header(output_dict)
148+
output_template = yaml_dump(output_dict)
150149
self.assertEqual(input_template, output_template)
151150

152151
def test_yaml_merge_tag(self):
@@ -183,7 +182,7 @@ def test_unroll_yaml_anchors(self):
183182
' Foo: bar\n'
184183
' Spam: eggs\n'
185184
)
186-
actual = yaml_dump_without_header(template)
185+
actual = yaml_dump(template)
187186
self.assertEqual(actual, expected)
188187

189188
def test_yaml_dump_quotes_boolean_strings(self):
@@ -194,26 +193,4 @@ def test_yaml_dump_quotes_boolean_strings(self):
194193
]
195194
for bool_as_string in bools_as_strings:
196195
self.assertEqual(
197-
yaml_dump_without_header(bool_as_string),
198-
"'%s'\n" % bool_as_string)
199-
200-
def test_yaml_dump_quotes_octal_strings(self):
201-
octals_as_strings = [
202-
'01111111', '02222222', '03333333',
203-
'04444444', '05555555', '06666666',
204-
'07777777', '08888888', '09999999'
205-
]
206-
for octal_as_string in octals_as_strings:
207-
self.assertEqual(
208-
yaml_dump_without_header(octal_as_string),
209-
"'%s'\n" % octal_as_string)
210-
211-
def test_yaml_dump_include_1_1_header(self):
212-
template = {"Resources": {"Foo": "Bar"}}
213-
expected = (
214-
'%YAML 1.1\n---\n'
215-
'Resources:\n'
216-
' Foo: Bar\n'
217-
)
218-
actual = yaml_dump(template)
219-
self.assertEqual(actual, expected)
196+
yaml_dump(bool_as_string), "'%s'\n" % bool_as_string)

0 commit comments

Comments
 (0)