You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As mentioned in #131507 and #130645, theming support is an important piece of the puzzle in introducing color to Python output in the terminal.
Design goals
Simplicity of use with type safety
It should be as easy to use theming as to operate with colors by hand.
User configurability through $PYTHONSTARTUP
It should be trivially possible for the user to set up their own favorite colors in their $PYTHONSTARTUP script.
Theme exchange through PyPI
It should be possible for the user to pip install theme-something and set_theme(theme_something.theme) in their $PYTHONSTARTUP and be done with it.
Forward-compatibility of old themes
It should be possible for the user to keep using a theme from 3.14 in future versions of Python without changes. The new color definitions would be automatically taken from the default theme.
Immutability of theme objects
It should be impossible for user code to modify the default theme in place as this can lead to inconsistencies.
Implementation
Types
The _colorize module contains a Theme dataclass, which groups theme sections together. It's immutable. Each theme section corresponds to a theming use case, like the REPL or Argparse. Each theme section is its own dataclass as it will contain keys unique to the use case. Every theme section is also immutable.
For convenience, each theme section inherits from a ThemeSection mixin that provides the theme section with the capability to create a colorless variant or a copy with only some keys modified.
The _colorize module also exposes a default_theme and theme_no_color globals. Those can be copied to create derivative themes.
Using themes in stdlib code
Python standard libraries using color themes should be using the get_theme() function exclusively. This function will return the currently configured theme. If the end user disabled colors or stdout is not a TTY, the returned theme will contain empty strings for every theme section key. This allows the standard library using color themes to program assuming colors everywhere, and they will turn into no-ops in environments where color should not be emitted.
Implementations can optionally also directly call can_colorize() to avoid costly color-specific operations like parsing source code to syntax highlight it.
Importantly, standard libraries should never cache output of get_theme() and can_colorize() as those values can change during the execution of the program:
the end user can switch out a theme after your standard library was already imported;
they can switch out a theme in the interactive shell or through PDB;
can_colorize() might return a different answer if os.environ changes or the console mode changes (possible on Windows).
Configuration
The end user can import _colorize in their $PYTHONSTARTUP script and use set_theme() to set it to a new value. This new value can be:
a theme created in-place using regular dataclass instantiation and the convenience .copy_with() methods;
a theme imported from another module, possibly installed from PyPI.
In non-interactive use of Python, the $PYTHONSTARTUP file is not executed. For such use cases, customizing the theme can be achieved through a sitecustomize or a usercustomize module.
Rejected Ideas
Introduce a new configuration file
There are already too many configuration files. Given Python's extensible nature, a format like TOML or INI would become insufficient. This would require a lengthy discussion, in which time no theming support would be available to users at all.
Use simple dictionaries
This is easier to implement but makes type-checking harder, allows user code to override color codes at will (which can lead to broken terminal output), and provides less conveniences for creating derivative themes from the default one.
Introduce a theme like $LS_COLORS to $PYTHON_COLORS
Since there are many theming sections, the resulting string format would have to be very human-unfriendly. Setting it would affect Python-based applications that might want to handle theming themselves. Priority would be unclear.
Open issues
Rename _colorize to colorize
There's frankly very little time for this before Python 3.14 beta 1. I would rather keep the library name underscored for 3.14 and gather feedback from the community on the format and behavior, and introduce a stable API in Python 3.15, keeping _colorize for backward compatibility.
As mentioned in #131507 and #130645, theming support is an important piece of the puzzle in introducing color to Python output in the terminal.
Design goals
Simplicity of use with type safety
It should be as easy to use theming as to operate with colors by hand.
User configurability through $PYTHONSTARTUP
It should be trivially possible for the user to set up their own favorite colors in their
$PYTHONSTARTUP
script.Theme exchange through PyPI
It should be possible for the user to
pip install theme-something
andset_theme(theme_something.theme)
in their$PYTHONSTARTUP
and be done with it.Forward-compatibility of old themes
It should be possible for the user to keep using a theme from 3.14 in future versions of Python without changes. The new color definitions would be automatically taken from the default theme.
Immutability of theme objects
It should be impossible for user code to modify the default theme in place as this can lead to inconsistencies.
Implementation
Types
The
_colorize
module contains aTheme
dataclass, which groups theme sections together. It's immutable. Each theme section corresponds to a theming use case, like the REPL or Argparse. Each theme section is its own dataclass as it will contain keys unique to the use case. Every theme section is also immutable.For convenience, each theme section inherits from a
ThemeSection
mixin that provides the theme section with the capability to create a colorless variant or a copy with only some keys modified.The
_colorize
module also exposes adefault_theme
andtheme_no_color
globals. Those can be copied to create derivative themes.Using themes in stdlib code
Python standard libraries using color themes should be using the
get_theme()
function exclusively. This function will return the currently configured theme. If the end user disabled colors or stdout is not a TTY, the returned theme will contain empty strings for every theme section key. This allows the standard library using color themes to program assuming colors everywhere, and they will turn into no-ops in environments where color should not be emitted.Implementations can optionally also directly call
can_colorize()
to avoid costly color-specific operations like parsing source code to syntax highlight it.Importantly, standard libraries should never cache output of
get_theme()
andcan_colorize()
as those values can change during the execution of the program:can_colorize()
might return a different answer ifos.environ
changes or the console mode changes (possible on Windows).Configuration
The end user can import
_colorize
in their$PYTHONSTARTUP
script and useset_theme()
to set it to a new value. This new value can be:.copy_with()
methods;In non-interactive use of Python, the
$PYTHONSTARTUP
file is not executed. For such use cases, customizing the theme can be achieved through asitecustomize
or ausercustomize
module.Rejected Ideas
Introduce a new configuration file
There are already too many configuration files. Given Python's extensible nature, a format like TOML or INI would become insufficient. This would require a lengthy discussion, in which time no theming support would be available to users at all.
Use simple dictionaries
This is easier to implement but makes type-checking harder, allows user code to override color codes at will (which can lead to broken terminal output), and provides less conveniences for creating derivative themes from the default one.
Introduce a theme like
$LS_COLORS
to$PYTHON_COLORS
Since there are many theming sections, the resulting string format would have to be very human-unfriendly. Setting it would affect Python-based applications that might want to handle theming themselves. Priority would be unclear.
Open issues
Rename
_colorize
tocolorize
There's frankly very little time for this before Python 3.14 beta 1. I would rather keep the library name underscored for 3.14 and gather feedback from the community on the format and behavior, and introduce a stable API in Python 3.15, keeping
_colorize
for backward compatibility.Linked PRs
The text was updated successfully, but these errors were encountered: