Skip to content

gh-149189: Modern defaults for pprint#149190

Open
hugovk wants to merge 8 commits intopython:mainfrom
hugovk:3.15-pprint-defaults
Open

gh-149189: Modern defaults for pprint#149190
hugovk wants to merge 8 commits intopython:mainfrom
hugovk:3.15-pprint-defaults

Conversation

@hugovk
Copy link
Copy Markdown
Member

@hugovk hugovk commented Apr 30, 2026

Given:

from pprint import pprint

config = {
    "database": {
        "host": "db.example.com",
        "port": 5432,
        "options": {
            "connect_timeout": 5,
            "pool_size": 10,
            "ssl": True,
            "sslmode": "verify-full",
        },
    },
    "logging": {
        "level": "INFO",
        "file": "/var/log/app.log",
        "handlers": {
            "console": {"enabled": True},
            "file": {"rotate": True, "max_bytes": 1048576},
        },
    },
}

print("===== expand=False =====")
print()
pprint(config)

print()

print("===== expand=True =====")
print()
pprint(config, expand=True)

main produces:

{'database': {'host': 'db.example.com',
              'options': {'connect_timeout': 5,
                          'pool_size': 10,
                          'ssl': True,
                          'sslmode': 'verify-full'},
              'port': 5432},
 'logging': {'file': '/var/log/app.log',
             'handlers': {'console': {'enabled': True},
                          'file': {'max_bytes': 1048576, 'rotate': True}},
             'level': 'INFO'}}

This PR produces:

{
    'database': {
        'host': 'db.example.com',
        'options': {'connect_timeout': 5, 'pool_size': 10, 'ssl': True, 'sslmode': 'verify-full'},
        'port': 5432,
    },
    'logging': {
        'file': '/var/log/app.log',
        'handlers': {'console': {'enabled': True}, 'file': {'max_bytes': 1048576, 'rotate': True}},
        'level': 'INFO',
    },
}

@hugovk hugovk requested a review from cjw296 as a code owner April 30, 2026 16:00
@hugovk hugovk added the stdlib Standard Library Python modules in the Lib/ directory label Apr 30, 2026
@read-the-docs-community
Copy link
Copy Markdown

read-the-docs-community Bot commented Apr 30, 2026

@hugovk hugovk force-pushed the 3.15-pprint-defaults branch from 89842d3 to 3f04477 Compare April 30, 2026 16:15
Copy link
Copy Markdown
Member

@sobolevn sobolevn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully support this feature! I never used pprint, because I always thought that it is not actually pretty. Multilple nesting levels were really hard to read, indentation was hard to follow.

The only downside that, I think, we should empathise in the "What's new" is that this can break doctests. Let's maybe add a section on how one can restore the old behavior for all versions of Python?

@hugovk
Copy link
Copy Markdown
Member Author

hugovk commented May 2, 2026

It would only break doctests if you're using pprint in them, so I've added a more generic "Use indent=1, width=80, expand=False to retain old output" to What's New.

Copy link
Copy Markdown
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do love this new format and agree that people will be happy with it. While this could break tests, I don't think we would have to worry much about that but I am concerned about the fact that explicit compact=True usages will now raise ValueError. For doctests, it's ok, but if someone is using pprint.pprint to render something to the user in an application, that application will no more be running. Sure, it's acceptable because we are using doing this in a new Python version, but legacy CLis that are no more maintained may break =/

Comment thread Doc/library/pprint.rst Outdated
Comment on lines +181 to +184
[ [ 'spam', 'eggs', 'lumberjack',
'knights', 'ni'],
'spam', 'eggs', 'lumberjack',
'knights', 'ni']
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this one, I would suggest passing indent=1 and remove indent=4 in the previous test.

Comment thread Doc/library/pprint.rst Outdated
.. versionchanged:: next
Changed default *indent* from 1 to 4,
default *width* from 80 to 88,
and default *expand* from ``False`` to ``True``.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default for expand does not need to be documented. Otherwise we have duplicate 3.15 entries.

Comment thread Lib/pprint.py Outdated
Comment on lines +72 to +73
compact=False,
expand=True,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this break anyone using compact=True explicitl?

@hugovk
Copy link
Copy Markdown
Member Author

hugovk commented May 2, 2026

I do love this new format and agree that people will be happy with it. While this could break tests, I don't think we would have to worry much about that but I am concerned about the fact that explicit compact=True usages will now raise ValueError. For doctests, it's ok, but if someone is using pprint.pprint to render something to the user in an application, that application will no more be running. Sure, it's acceptable because we are using doing this in a new Python version, but legacy CLis that are no more maintained may break =/

Good point. How about we remove the ValueError and let compact win if both are true?

@picnixz
Copy link
Copy Markdown
Member

picnixz commented May 2, 2026

Good point. How about we remove the ValueError and let compact win if both are true?

What about an explicit compact=False then? it wouldn't be recognized as a "forced" compact mode since expand=True is the default. In this case, we would need compact and expand being None to detect whether something was passed explicitly or not. But those kind of interfaces are always prone to errors and hard to maintain (curses is full of those mixed interfaces with weird overloads) and anyone subclassing PrettyPrinter would have different defaults.

I don't have a solution for this problem though. Making compact=False now ignored by default seems to be the best idea to do, but people with an explicit compact=False for whatever reasons will have their code possibly broken (instead, they should have an explicit expand=False). For this specific issue, I think it's more reasonable to accept the change.

@hugovk
Copy link
Copy Markdown
Member Author

hugovk commented May 3, 2026

I've removed the ValueError when both compact=expand=True, and in that case, compact=True takes precendence. How's that?

@picnixz
Copy link
Copy Markdown
Member

picnixz commented May 3, 2026

I thought about it yesterday offline and I either don't remember or don't undertand why we added expand. Isn't the intent of expand to have some "non-compact" representation, i.e. an "explicit" compact=False?

Pre-3.15, we have compact=False so this means that the output is not meant to be compact. Why did we needed to add expand together as it was meant to be incompatible with compact and why not just changing the meaning of compact?

The way I see it: compact=False has some "legacy" non-compact style. expand is the "newer" non-compact style. So instead of having compact and expand, how about just keeping compact and maybe adding some legacy parameter to change the meaning of compact? That way, we won't break existing code and people would only need to put legacy=True if they want "compact" to have the pre-3.15 meaning. I don't see why a new expand paramter was necessary.

In my ideal world, I would have some style option an pre-define some styles so that we can have whatever meanings we want. But that's maybe too late =/

If we want to retain those two parameters together, I think what you did is the best. But please document that passing compact=False has no effect by default and that users should use expand=False instead now (or we can use compact=None to detect whether it's been given)

@hugovk
Copy link
Copy Markdown
Member Author

hugovk commented May 3, 2026

I thought about it yesterday offline and I either don't remember or don't understand why we added expand. Isn't the intent of expand to have some "non-compact" representation, i.e. an "explicit" compact=False?

We added it to basically make the output nicer.

Pre-3.15, we have compact=False so this means that the output is not meant to be compact. Why did we needed to add expand together as it was meant to be incompatible with compact and why not just changing the meaning of compact?

I guess it was an addition because that's the incremental step and didn't want to change the default. But now we are! So perhaps we should remove expand altogether and remove that old non-compact format?

The way I see it: compact=False has some "legacy" non-compact style. expand is the "newer" non-compact style. So instead of having compact and expand, how about just keeping compact and maybe adding some legacy parameter to change the meaning of compact? That way, we won't break existing code and people would only need to put legacy=True if they want "compact" to have the pre-3.15 meaning. I don't see why a new expand paramter was necessary.

Hmm, I'm not so keen on legacy=True. Legacy is just the old way, what about the next time we change something and make it legacy..?

If we want to retain those two parameters together, I think what you did is the best. But please document that passing compact=False has no effect by default and that users should use expand=False instead now (or we can use compact=None to detect whether it's been given)

Added the note for now.

@picnixz
Copy link
Copy Markdown
Member

picnixz commented May 3, 2026

But now we are! So perhaps we should remove expand altogether and remove that old non-compact format?

Honestly, I would say yes. If, in the future, we want to have fine-grained control, we should add a unique style argument that would dictate how the formatter behaves (through callbacks for instance). The default compact=False looks already weird IMO; while prettiness is indeed subjective, having a balanced output like IDEs do for non-compact mode is honestly better.

The only thing we would break is:

  • People expecting the default output to be compact would have their tests broken.
  • It could possibly make the output much nested and truncated depending on how people deal with it. With an expanded format, you have less space to put your items on each line. With compact you could expact something fitting.
  • CLIs that truncate the number of lines may now truncate too many lines. Assume you use pprint.pprint(something).splitlines()[:100], then before we probably have 100 relevant lines, but after we may have only the first items (compar the outputs of print(*pprint.pformat({"a":[4,5,6]*12}, compact=False, width=40).splitlines(keepends=True)[:5]) with true/false compact).

Here, I'm playing the devil's advocate but I wanted to highilght those observations for the future us. My personal take is: remove expand and change the meaning of compact. Having expand and compact at the same time, but having expand silently incompatible with compact and still having explicit compact=False now ignored makes it worse IMO.

Another reason why I would just change the meaning of compact=False is:

  • Pre-3.15, compact=False is already the default!
  • We just change what "non-compact" means.
  • But we respect users who want explicit non-compact mode via compact=False and users that really want a compact form via compact=True.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting merge stdlib Standard Library Python modules in the Lib/ directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants