python / python/cpython

Subclassing PrettyPrinter.format doesn't work in all cases

Open
#132,855 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stdlib type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

See also the earlier fixed https://github.com/python/cpython/issues/73036. This appears to have only been a partial fix, or it has regressed.

import collections
import pprint
import types


SENTINAL = object()


class _hashable:
    def __hash__(self):
        return 1


HASHABLE_SENTINAL = _hashable()


class CustomPrettyPrinter(pprint.PrettyPrinter):
    def format(self, obj, context, maxlevels, level):
        if obj is SENTINAL:
            return "SENTINAL", True, False
        elif obj is HASHABLE_SENTINAL:
            return "HASHABLE_SENTINAL", True, False
        else:
            return super().format(obj, context, maxlevels, level)


if __name__ == "__main__":
    printer = CustomPrettyPrinter(sort_dicts=False)

    test_data = {
        "item": SENTINAL,
        "dict": {"a": SENTINAL},
        "dict_key": {HASHABLE_SENTINAL: 1},
        "OrderedDict": collections.OrderedDict({"a": SENTINAL}),
        "OrderedDict_key": collections.OrderedDict({HASHABLE_SENTINAL: 1}),
        "list": [SENTINAL],
        "tuple": (SENTINAL,),
        "set": {SENTINAL},
        "frozenset": frozenset({SENTINAL}),
        "mappingproxy": types.MappingProxyType({"a": SENTINAL}),
        "mappingproxy_key": types.MappingProxyType({HASHABLE_SENTINAL: 1}),
        "SimpleNamespace": types.SimpleNamespace(a=SENTINAL),
        "defaultdict": collections.defaultdict(list, {"a": SENTINAL}),
        "defaultdict_key": collections.defaultdict(list, {HASHABLE_SENTINAL: 1}),
        "Counter": collections.Counter({"a": SENTINAL}),
        "Counter_key": collections.Counter({HASHABLE_SENTINAL: 1}),
        "deque": collections.deque([SENTINAL]),
    }

    printer.pprint(test_data)

This outputs:

{'item': SENTINAL,
 'dict': {'a': SENTINAL},
 'dict_key': {HASHABLE_SENTINAL: 1},
 'OrderedDict': OrderedDict({'a': <object object at 0x1041f4660>}),
 'OrderedDict_key': OrderedDict([(HASHABLE_SENTINAL, 1)]),
 'list': [SENTINAL],
 'tuple': (SENTINAL,),
 'set': {<object object at 0x1041f4660>},
 'frozenset': frozenset({<object object at 0x1041f4660>}),
 'mappingproxy': mappingproxy({'a': <object object at 0x1041f4660>}),
 'mappingproxy_key': mappingproxy({HASHABLE_SENTINAL: 1}),
 'SimpleNamespace': namespace(a=<object object at 0x1041f4660>),
 'defaultdict': defaultdict(<class 'list'>,
                            {'a': SENTINAL}),
 'defaultdict_key': defaultdict(<class 'list'>,
                                {HASHABLE_SENTINAL: 1}),
 'Counter': Counter({'a': <object object at 0x1041f4660>}),
 'Counter_key': Counter({<__main__._hashable object at 0x1045e4d70>: 1}),
 'deque': deque([<object object at 0x1041f4660>])}

Every instance of < in that output is a bug.

CPython versions tested on:

3.13

Operating systems tested on:

macOS

Linked PRs
  • gh-133238

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the reproducer and inspecting pprint.PrettyPrinter.format, especially how it handles the listed container types and their contents. Compare each output with the expected custom formatting, then add regression coverage for the cases where the override is bypassed. Done means every shown or repr from the reproducer honors the subclass's format method.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.