Skip to content

Commit b331439

Browse files
committed
False-y names are treated as positional arguments.
Also add some tests for some corner cases.
1 parent 0d8320b commit b331439

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

Lib/pprint.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,11 +643,22 @@ def _format_pprint(self, object, method, context, maxlevels, level):
643643
formatted, is_readable, _ = self.format(value, context, maxlevels, level + 1)
644644
parts.append(f"{name}={formatted}")
645645
readable = readable and is_readable
646-
case (name, value):
646+
case (str() as name, value) if name:
647647
# Keyword argument. Always show.
648648
formatted, is_readable, _ = self.format(value, context, maxlevels, level + 1)
649649
parts.append(f"{name}={formatted}")
650650
readable = readable and is_readable
651+
case (name, value) if not name:
652+
# 2-tuple with a false-y name: treat as positional.
653+
formatted, is_readable, _ = self.format(value, context, maxlevels, level + 1)
654+
parts.append(formatted)
655+
readable = readable and is_readable
656+
case (name, value):
657+
# Truthy non-string name is an error.
658+
raise ValueError(
659+
f"__pprint__ yielded a 2-tuple with "
660+
f"non-string name: {name!r}"
661+
)
651662
case _:
652663
# Positional argument.
653664
formatted, is_readable, _ = self.format(item, context, maxlevels, level + 1)

Lib/test/test_pprint.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,46 @@ def __pprint__(self):
16021602
self.assertTrue(pprint.isreadable(Readable()))
16031603
self.assertFalse(pprint.isreadable(Unreadable()))
16041604

1605+
def test_pprint_protocol_falsey_names(self):
1606+
# Any 2-tuple form with a falsey name gets treated as a positional argument.
1607+
class IsFalse:
1608+
def __bool__(self):
1609+
return False
1610+
1611+
# 2-tuple form with falsey names are treated as positional.
1612+
class PositionalTuples:
1613+
def __pprint__(self):
1614+
yield None, (1, 2)
1615+
yield False, (3, 4)
1616+
yield 0, (5, 6)
1617+
yield IsFalse(), (7, 8)
1618+
1619+
stream = io.StringIO()
1620+
pprint.pprint(PositionalTuples(), stream=stream)
1621+
self.assertEqual(
1622+
stream.getvalue(),
1623+
'PositionalTuples((1, 2), (3, 4), (5, 6), (7, 8))\n'
1624+
)
1625+
1626+
def test_pprint_protocol_truthy_nonstring_names(self):
1627+
# 2-tuple form with truthy, non-str name is an error.
1628+
class BrokenPrinter_1:
1629+
def __pprint__(self):
1630+
yield 7, 'hello'
1631+
1632+
self.assertRaises(ValueError, pprint.pprint, BrokenPrinter_1())
1633+
1634+
# The name argument must be exactly a str.
1635+
class Strable:
1636+
def __str__(self):
1637+
yield 'strable'
1638+
1639+
class BrokenPrinter_2:
1640+
def __pprint__(self):
1641+
yield Strable(), 'hello'
1642+
1643+
self.assertRaises(ValueError, pprint.pprint, BrokenPrinter_2())
1644+
16051645

16061646
class DottedPrettyPrinter(pprint.PrettyPrinter):
16071647

0 commit comments

Comments
 (0)