Skip to content
Open
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
7 changes: 4 additions & 3 deletions devtools/prettier.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def _str_lines(self, lines: Iterable[str | bytes], indent_current: int, indent_n
self._stream.write(indent_current * self._c + ')')

def _wrap_lines(self, s: str | bytes, indent_new: int) -> Generator[str | bytes, None, None]:
width = self._width - indent_new - 3
width = max(1, self._width - indent_new - 3)
for line in s.splitlines(True):
start = 0
for pos in range(width, len(line), width):
Expand Down Expand Up @@ -285,13 +285,14 @@ def _format_raw(self, _: Any, value_repr: str, indent_current: int, indent_new:
lines = value_repr.splitlines(True)
if len(lines) > 1 or (len(value_repr) + indent_current) >= self._width:
self._stream.write('(\n')
wrap_at = self._width - indent_new
wrap_at = max(1, self._width - indent_new)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
prefix = indent_new * self._c

from textwrap import wrap

for line in lines:
sub_lines = wrap(line, wrap_at)
# At width one, whitespace must occupy its own line instead of being dropped by textwrap.
sub_lines = line.rstrip('\r\n') if wrap_at == 1 else wrap(line, wrap_at)
for sline in sub_lines:
self._stream.write(prefix + sline + '\n')
self._stream.write(indent_current * self._c + ')')
Expand Down
52 changes: 52 additions & 0 deletions tests/test_prettier.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,3 +513,55 @@ def test_ast_expr():

def test_ast_module():
assert pformat(ast.parse('print(1, 2, round(3))')).startswith('Module(\n body=[')


@pytest.mark.parametrize('width', [7, 11, 15])
@pytest.mark.parametrize('value', ['abcdefghijk', b'abcdefghijk'])
def test_nested_string_exhausted_width(width: int, value: str | bytes):
output = PrettyFormat(width=width)([value])
assert ast.literal_eval(output) == [value]


@pytest.mark.parametrize('width', [7, 11, 15])
def test_nested_bytearray_exhausted_width(width: int):
value = bytearray(b'abcdefghijk')
output = PrettyFormat(width=width)([value])
node = ast.parse(output, mode='eval').body
assert isinstance(node, ast.List)
call = node.elts[0]
assert isinstance(call, ast.Call)
assert ast.literal_eval(call.args[0]) == bytes(value)


@pytest.mark.parametrize('width', [12, 120])
@pytest.mark.parametrize('depth', [2, 30])
@pytest.mark.parametrize('highlight', [False, True])
def test_nested_raw_repr_exhausted_width(width: int, depth: int, highlight: bool):
class LongRepr:
def __repr__(self) -> str:
return 'Custom(value=' + 'x' * 120 + ')'

leaf = LongRepr()
value: Any = leaf
for _ in range(depth):
value = [value]
output = PrettyFormat(width=width)(value, highlight=highlight)
assert repr(leaf) in ''.join(strip_ansi(output).split())


@pytest.mark.parametrize('width,depth', [(12, 2), (13, 2), (120, 30)])
@pytest.mark.parametrize('highlight', [False, True])
@pytest.mark.parametrize('payload', ['alpha beta gamma', ' leading and trailing ', 'alpha\tbeta'])
def test_exhausted_raw_repr_preserves_whitespace(width: int, depth: int, highlight: bool, payload: str):
class RawRepr:
def __repr__(self) -> str:
return f"Custom('{payload}')"

leaf = RawRepr()
value: Any = leaf
for _ in range(depth):
value = [value]
output = strip_ansi(PrettyFormat(width=width)(value, highlight=highlight))
prefix = ' ' * ((depth + 1) * 4)
content = ''.join(line[len(prefix) :] for line in output.splitlines() if line.startswith(prefix))
assert content == repr(leaf)
Loading