From 1e6fea0db4279eb18a0a1b2bd3c7f2d92e5ec55d Mon Sep 17 00:00:00 2001 From: kiwigitops Date: Thu, 28 May 2026 14:25:35 -0400 Subject: [PATCH] Fix nested config updates across non-option lines Signed-off-by: kiwigitops --- awscli/customizations/configure/writer.py | 41 ++++++++++------- .../customizations/configure/test_writer.py | 44 +++++++++++++++++++ 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/awscli/customizations/configure/writer.py b/awscli/customizations/configure/writer.py index 83825a53dab6..5381b82d589a 100644 --- a/awscli/customizations/configure/writer.py +++ b/awscli/customizations/configure/writer.py @@ -215,30 +215,37 @@ def _update_section_contents(self, contents, section_name, new_values): def _update_subattributes(self, index, contents, values, starting_indent): index += 1 + insert_at = index - 1 for i in range(index, len(contents)): line = contents[i] + if self.SECTION_REGEX.search(line) is not None: + # We've arrived at a new section so we can just write out all + # the values now. + self._insert_new_values(i - 1, contents, values, ' ') + return i match = self.OPTION_REGEX.search(line) - if match is not None: - current_indent = len( - match.group(1)) - len(match.group(1).lstrip()) - key_name = match.group(1).strip() - if key_name in values: - option_value = values[key_name] - new_line = '%s%s = %s\n' % (' ' * current_indent, - key_name, option_value) - contents[i] = new_line - del values[key_name] - if starting_indent == current_indent or \ - self.SECTION_REGEX.search(line) is not None: + if match is None: + insert_at = i + continue + current_indent = len( + match.group(1)) - len(match.group(1).lstrip()) + if starting_indent == current_indent: # We've arrived at the starting indent level so we can just # write out all the values now. self._insert_new_values(i - 1, contents, values, ' ') - break + return i + insert_at = i + key_name = match.group(1).strip() + if key_name in values: + option_value = values[key_name] + new_line = '%s%s = %s\n' % (' ' * current_indent, + key_name, option_value) + contents[i] = new_line + del values[key_name] else: - if starting_indent != current_indent: - # The option is the last option in the file - self._insert_new_values(i, contents, values, ' ') - return i + # The option is the last option in the file. + self._insert_new_values(insert_at, contents, values, ' ') + return insert_at def _insert_new_values(self, line_number, contents, new_values, indent=''): new_contents = [] diff --git a/tests/unit/customizations/configure/test_writer.py b/tests/unit/customizations/configure/test_writer.py index 9c85516762ef..9402e2b3eae9 100644 --- a/tests/unit/customizations/configure/test_writer.py +++ b/tests/unit/customizations/configure/test_writer.py @@ -295,6 +295,50 @@ def test_add_to_nested_with_nested_in_the_end(self): ' other = foo\n' ' signature_version = newval\n') + def test_add_to_empty_nested_block(self): + original = ( + '[default]\n' + 's3 =\n' + ) + self.assert_update_config( + original, {'__section__': 'default', + 's3': {'signature_version': 'newval'}}, + '[default]\n' + 's3 =\n' + ' signature_version = newval\n') + + def test_add_to_nested_block_with_comment(self): + original = ( + '[default]\n' + 's3 =\n' + '# comment\n' + ' other = foo\n' + ) + self.assert_update_config( + original, {'__section__': 'default', + 's3': {'signature_version': 'newval'}}, + '[default]\n' + 's3 =\n' + '# comment\n' + ' other = foo\n' + ' signature_version = newval\n') + + def test_add_to_nested_block_before_next_section(self): + original = ( + '[default]\n' + 's3 =\n' + '[profile foo]\n' + 'foo = bar\n' + ) + self.assert_update_config( + original, {'__section__': 'default', + 's3': {'signature_version': 'newval'}}, + '[default]\n' + 's3 =\n' + ' signature_version = newval\n' + '[profile foo]\n' + 'foo = bar\n') + def test_update_nested_attribute(self): original = ( '[default]\n'