diff --git a/.changes/next-release/bugfix-text-output-mixed-list.json b/.changes/next-release/bugfix-text-output-mixed-list.json new file mode 100644 index 000000000000..5de75f17fac6 --- /dev/null +++ b/.changes/next-release/bugfix-text-output-mixed-list.json @@ -0,0 +1,5 @@ +{ + "category": "``text`` output", + "description": "Fix ``AttributeError`` when ``--output text`` renders a list that contains both a scalar and an object, which a ``--query`` multi-select list such as ``[InstanceId, State]`` produces", + "type": "bugfix" +} diff --git a/awscli/text.py b/awscli/text.py index 6b915b0f8f2b..8ded80bb3a82 100644 --- a/awscli/text.py +++ b/awscli/text.py @@ -85,6 +85,11 @@ def _format_dict(scalar_keys, item, identifier, stream): def _all_scalar_keys(list_of_dicts): keys_seen = set() for item_dict in list_of_dicts: + if not isinstance(item_dict, dict): + # The list is only required to contain *at least* one dict, + # so skip over any elements that aren't dicts. They are + # rendered on their own by _format_text. + continue for key, value in item_dict.items(): if not isinstance(value, (dict, list)): keys_seen.add(key) diff --git a/tests/unit/test_text.py b/tests/unit/test_text.py index c08e2e23033d..0e748286d23a 100644 --- a/tests/unit/test_text.py +++ b/tests/unit/test_text.py @@ -219,6 +219,22 @@ def test_deeply_nested_with_identifier(self): 'FOO\th\n' ) + def test_dicts_mixed_with_scalars(self): + # A --query multi-select list such as '[InstanceId, State]' + # produces a list holding both a scalar and a dict. + self.assert_text_renders_to( + ['i-123', dict(Code=16, Name='running')], + 'i-123\n' + '16\trunning\n' + ) + + def test_dicts_mixed_with_lists(self): + self.assert_text_renders_to( + [['a', 'b'], dict(Code=16, Name='running')], + 'a\tb\n' + '16\trunning\n' + ) + if __name__ == '__main__': unittest.main()