Skip to content
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
6 changes: 6 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Version 2.9.0

(released on 2026-01-26)

- Add optional `convert_to_undecoded_string` preprocessor.

## Version 2.8.2

(released on 2026-01-26)
Expand Down
20 changes: 20 additions & 0 deletions cli_helpers/tabular_output/preprocessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,26 @@ def convert_to_string(data, headers, **_):
)


def convert_to_undecoded_string(data, headers, **_):
"""Convert all *data* and *headers* to hex, if needed.

Binary data is converted to a hexadecimal representation via
:func:`binascii.hexlify`.

Unlike convert_to_string(), None values are left as Nones.

:param iterable data: An :term:`iterable` (e.g. list) of rows.
:param iterable headers: The column headers.
:return: The processed data and headers.
:rtype: tuple

"""
return (
([utils.to_undecoded_string(v) for v in row] for row in data),
[utils.to_undecoded_string(h) for h in headers],
)


def override_missing_value(
data,
headers,
Expand Down
24 changes: 24 additions & 0 deletions cli_helpers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ def bytes_to_string(b):
return b


def to_hex_if_bin(b):
"""Convert bytes *b* to a string.

Pass b through if not bytes.

"""
if isinstance(b, binary_type):
return "0x" + binascii.hexlify(b).decode("ascii")
return b


def to_string(value):
"""Convert *value* to a string."""
if isinstance(value, binary_type):
Expand All @@ -42,6 +53,19 @@ def to_string(value):
return text_type(value)


def to_undecoded_string(value):
"""Convert *value* to an undecoded string, respecting Nones."""
# preserve Nones so that
# * this can run before override_missing_value when stringifying
# * Nones are preserved in formats such as CSV
if value is None:
return None
elif isinstance(value, binary_type):
return to_hex_if_bin(value)
else:
return text_type(value)


def truncate_string(value, max_width=None, skip_multiline_string=True):
"""Truncate string values."""
if skip_multiline_string and isinstance(value, text_type) and "\n" in value:
Expand Down
11 changes: 11 additions & 0 deletions tests/tabular_output/test_preprocessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
align_decimals,
bytes_to_string,
convert_to_string,
convert_to_undecoded_string,
quote_whitespaces,
override_missing_value,
override_tab_value,
Expand Down Expand Up @@ -38,6 +39,16 @@ def test_convert_to_string():
assert expected == (list(results[0]), results[1])


def test_convert_to_undecoded_string():
"""Test the convert_to_undecoded_string() function."""
data = [[1, "John"], [2, b"Jill"], [3, None]]
headers = [0, "name"]
expected = ([["1", "John"], ["2", "0x4a696c6c"], ["3", None]], ["0", "name"])
results = convert_to_undecoded_string(data, headers)

assert expected == (list(results[0]), results[1])


def test_override_missing_values():
"""Test the override_missing_values() function."""
data = [[1, None], [2, "Jill"]]
Expand Down
Loading