Skip to content

Update JSON formatter to encode bytes as Base64 strings. #9438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/bugfix-Formatter-25003.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "bugfix",
"category": "Formatter",
"description": "Update JSON formatter to encode raw bytes as UTF-8."
}
8 changes: 6 additions & 2 deletions awscli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import base64
import contextlib
import csv
import datetime
Expand Down Expand Up @@ -185,11 +186,14 @@ def operation_uses_document_types(operation_model):


def json_encoder(obj):
"""JSON encoder that formats datetimes as ISO8601 format."""
"""JSON encoder that formats datetimes as ISO8601 format
and encodes bytes to UTF-8 Base64 string."""
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj, bytes):
return base64.b64encode(obj).decode("utf-8")
else:
return obj
raise TypeError('Encountered unrecognized type in JSON encoder.')


@contextlib.contextmanager
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/output/test_json_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import base64
import contextlib
import io
import sys
from botocore.compat import json
import platform
from awscli.formatter import JSONFormatter
Expand Down Expand Up @@ -127,3 +131,26 @@ def test_fully_buffered_handles_io_error(self):
# we still should have called the flush() on the
# stream.
fake_closed_stream.flush.assert_called_with()


class TestBinaryData(unittest.TestCase):
def test_binary_data_gets_base64_encoded(self):
args = mock.Mock(query=None)
raw_bytes = b'foo'
response = {'BinaryValue': raw_bytes}
stdout_b = io.BytesIO()
stdout = io.TextIOWrapper(stdout_b, newline='\n')
formatter = JSONFormatter(args)

with contextlib.redirect_stdout(stdout):
formatter('command-name', response, sys.stdout)
stdout.flush()

assert (
stdout_b.getvalue()
== (
'{\n'
f' "BinaryValue": "{base64.b64encode(raw_bytes).decode("utf-8")}"\n'
'}\n'
).encode()
)
Loading