Pre-PEP: `textfmt module`: A Standardized Approach to Terminal Text Styling

*Note this is my first pep may have made some mistakes

PEP: NNNN
Title: The textfmt module: A Standardized Approach to Terminal Text Styling
Author: Rihaan Meher
Discussions-To: Pending
Status: Draft
Type: Standards Track
Created: 25-May-2025
Python-Version: 3.15

Abstract

The textfmt module provides a standardized approach to text formatting in Python console applications. It simplifies ANSI escape code usage by offering named attributes and intuitive functions for styling text. By introducing a built-in formatting utility, Python enhances its capabilities for CLI-based applications without requiring third-party dependencies.

Motivation

Currently, developers rely on manual ANSI escape sequences or external libraries to format terminal output. This leads to inconsistent implementations and redundancy across projects. A standardized module would unify text styling conventions, improving usability in debugging, logging, and command-line applications.

Rationale

A built-in solution for text styling in Python aligns with its philosophy of readability and simplicity. While third-party libraries exist, a standard module ensures uniformity and avoids compatibility issues. Given Python’s widespread use in scripting and CLI development, textfmt would streamline terminal output formatting, making structured text display more accessible.

Additionally, textfmt would enhance readability and code maintainability by replacing raw escape sequences with meaningful attributes like textfmt.bold, textfmt.red, and textfmt.underline.

1. ANSI Alias Class

A class containing named attributes for common ANSI escape sequences:

python

import textfmt

print(textfmt.bold + "Important!" + textfmt.reset)
print(textfmt.red + "Error detected." + textfmt.reset)

2. Formatting Functions

Helper functions for applying styles cleanly:

print(textfmt.style("Warning!", bold=True, fg="yellow"))
print(textfmt.style("Success!", fg="green"))

3. Terminal Compatibility Handling

Detects terminal support and falls back gracefully when ANSI codes are unsupported.

Examples

  • Logging Enhancement:

python

logger.info(textfmt.green + "Operation succeeded" + textfmt.reset)
logger.error(textfmt.red + "Fatal error!" + textfmt.reset)
  • Styled User Prompts:

python

user_input = input(textfmt.blue + "Enter your choice: " + textfmt.reset)

Security Implications

None

Backwards Compatibility

No compatibility issues for relevant systems. Can be easily backported if needed

How to Teach This

The textfmt module will be part of Python’s standard library, ensuring consistent text formatting across applications. Code editors should update syntax highlighting to recognize textfmt attributes, improving readability. IDEs can integrate format-aware previews to help developers visualize styled output in real time.

1 Like

That suggests that you’re doing something on import. It would be better to separate initialization from import even if it makes your base cases look slightly more complicated.

Have you look at how Rich does it?

1 Like

Good idea! I could add a method like textfmt.start() or init etc.

What do you think of this idea though?

That has the problem of global state. Rich has a Console type for this.

As for idea, I’ll probably just keep using Rich.

Text colorization is needed in enough places that CPython already has a private _colorize module for it, complete with a can_colorize function to test if a stream is colorizable, and a ThemeSection class to manage a set of colorizations as a theme.

It’s used by many stdlib modules with great consistency and I think it covers enough common use cases to be promoted as a public library. The fact that it sticks to ANSI color codes means that it’s going to be stable and compatible enough to be practically maintenance-free. For all the fancier needs, for sure one can install rich instead.

2 Likes

Rich is good to a certain extent. As blshing said, it is useful enough to be promoted as a public library, but it would be useful to include this in the stdlib to reduce verbosity and promote more readable py applications. For simple things like showing error messages in a user Python application using rich is a waste

TL;DR:
For simple programs that benefit from this no one wants to download a whole formatting library

And these are just ideas. The actual PR (if becomes a PEP) will contain everything we need if people like the general idea.

Once I see support in the community I’ll draft a PEP

I don’t think this is core functionality that the stdlib needs to provide. This module would work fine as a PyPI package.

That said, I would maybe be interested in promoting the API to enable the Windows Virtual Terminal to public (cf colorama), but this wouldn’t require a PEP.

A

1 Like

But a good number of stdlib modules, many of which I’d consider as core functionalities (traceback, pdb, _pyrepl, argparse, doctest, among others), already use _colorize to format the output, so I don’t quite see a downside in simply exposing the module.

As mentioned, many stdlib modules rely on _colorize to format the output so it would not work as a PyPI package.

colorama is great but satisfies a need at a different layer so should be considered separately.

The standard library is a special case — the intent was that a PyPI package for formatting could be used by third-party code. Indeed, this demand is already satisfied by popular third-party libraries such as Rich and Colorama.

Promoting a private module to public brings with it far greater maintenance obligations that you’re asking the core developers to take on. Beyond this, and the obvious other considerations of locking in the API, it means that there is less incentive to innovate, as the stdlib option will become the default, even if other packages have better solutions.

A

2 Likes

Most people don’t actually need the bells and whistles provided by heavyweights such as Rich. The basic features offered by _colorize is enough for most people who just want to be able to format important words in bold or red.

tomllib offers only barebone functionalities and its documentation is not shy to point to 2 other popular PyPI packages for those who need more functionalities.

A public _colorize may do exactly the same to avoid taking on greater maintenance obligations.

I don’t see how the inclusion of optparse and argparse in the stdlib for example prevented PyPI from getting innovative CLI parsers such as click and fire.

1 Like

While I’m in favor of basic terminal color support, I would not publish _colorize.py as is. It’s quite recent history and we don’t want to limit python‘s internal ability to improve. It would be a different story if it existed for 5+ years without significant change. So any public API would need to be carefully chosen and designed.

3 Likes

Sorry, but waste of what?

For simple programs that benefit from this no one wants to download a whole formatting library

Why not? Computers are fast. Why learn two libraries?

2 Likes

I’m not too sure why you’re in favor of basic terminal color support while simultaneously wanting to wait for _colorize to grow to be more than it is now, exactly a module that offers basic terminal color support that has already proven its usefulness in a good variety of modules and formatting needs in the stdlib.

I think the basic API offered by _colorize is fine as is. Most of the time all that people want are for the color/bold/reset escape codes to be named, and for there to be an easy way to test if a stream can accept those codes. That’s it! And that’s exactly what _colorize has to offer in spades.

I’m not persuaded by the comparison with the rationale for tomllib. PEP 680’s Motivation notes that it was added to resolve the bootstrapping problem in the packaging ecosystem. That same problem doesn’t exist for terminal formatting – we have a full range of packages, from basic to complex to fully-featured ‘text UIs’. Especially with PEP 723 metadata, it is easy to require one of the aforementioned packages for formatting support, even in a throwaway scratch script. I think that the stdlib is right to expose support for colour at a higher level, e.g. argparse’s colour=True.

A

1 Like

It‘s not about „growing to be more“, but „proven to be the right approach“. In a private module, we can implement just what we need and refactor later if that was insufficient. A public module is much harder to change and therefore you have to think much harder on the design. For example, are you sure that this is the right way: Define the color constants as class attributes on ANSIColors, and then make a ColorCodes set? Would an Enum be better? Is it ok that NoColors = ColorCodes()? Theming is a complex topic. Are you confident that the implemented approach is reasonable?

3 Likes

There’s colorama and termcolor for those people.

3 Likes

I would appreciate minimal coloring support as „batteries included“, maybe not much more than a list of constants for the ANSI codes.

I have several small scripts that would benefit from colored output, but fiddling with the raw codes is cumbersome and unreadable. OTOH these scripts should work in any Python environment, so introducing a 3rd party dependency is a no-go.

2 Likes

I’m unclear where people are drawing the line here. You say your scripts should work in any Python environment. So what about Python 3.12? The only solutions that will work for older Pythons are coding your own, or using a 3rd party library.

If you mean “any suitably new Python environment”, what’s wrong with using _colorize? It’s undocumented and unsupported, certainly, but it’s available, and as long as you are willing to code around any changes that happen, it avoids any 3rd party library.

If you mean “users shouldn’t have to deal with creating virtual environments”, what’s wrong with script metadata and asking your users to use pipx run or uv run to run them?

I’d be happy to see the _colorize module exposed as a stdlib module once it’s stabilised and the core team feel comfortable supporting it. It seems unlikely in the extreme that any other text styling module will be added to the stdlib (why have two different solutions?) So if “documented and supported in the stdlib” is the requirement, I think “be patient and wait for _colorize” is the answer.

3 Likes

Yes, „any“ would not be perfect as long as such a hypothetical library would not be part of the minimal supported version. And even before that, requiring a minimal Python version is less constraining than requiring a third party package.

That’s exactly the point. I‘m not willing to code around any changes. That makes code brittle and is a maintenance burden.

These tools are not guaranteed to be available either. Also, there are systems without network access.

That‘s part of the discussion here. AFAICS there’s no ambition or consensus to publish anything related so far. The other aspect is that _colorize may contain more functionality than we‘re willing to support publically (e.g. I would not publish the theming any time soon if ever). The question is not to have „any other text styling module“, but whether there’s a minimal subset of text styling (e.g. the constants) that could be published rather soon and not to wait multiple years for a full stabilization of _colorize.

So: Is there a middle ground of text coloring functionality that (i) has a benefit to users and (ii) is sufficiently clear and small so that there are no design issues and a very low maintenance burden?

1 Like

Have you looked at _colorize.py? I just did, and if we ignore the theming support (which is experimental, so obviously not ready to expose) you’re left with:

  1. A list of ANSI colour escapes.
  2. can_colorize().

The former is just a bunch of data, but there are design questions around should it be an enum, or a class, or a dict, or
 Whether you are willing to use that depends on how concerned you are about that happening, and having to patch over it. If you are bothered, just copy/paste the definition.

The latter is full of design questions (do we support standard environment variables like NO_COLOR or TERM?) and platform specific code, which needs maintenance (I haven’t even looked at nt._supports_virtual_terminal, which is also private). It’s probably perfectly fine to use, as long as you don’t intend to raise issues or feature requests if what it does doesn’t match what you want :wink:

So no, I don’t think there’s much middle ground here.

Helper functions are the big problem, but there’s a huge space of possible designs here, which range from trivial (wrap a couple of ANSI sequences round some text) to highly complex (theme support, style merging, etc.) This is something that really should be developed outside of the stdlib. Colorama is probably the state of the art for low-level functionality, and Rich for high-level.

Maybe there’s a case for a “colorama lite” that drops all of the old-style Windows terminal compatibility, and focuses solely on ANSI. If that were created as a 3rd party library, and became popular enough to demonstrate that the API was good, it might possibly be something that could be integrated into the stdlib _colorize and made public. But someone needs to do the work, and it’s not likely to be the core devs (except in the sense that _colorize might develop into that - but that’s what I meant when I said “be patient”
)

I have a lot of sympathy for the position that requiring 3rd party libraries isn’t always as simple as people suggest, but in this case I think it’s the right thing to do, at least for now.

2 Likes