I’ve been working on a color addition to Plumbum for a little while, and I’d
like to share the basics of using it with you now. This library was originally
built around a special str
subclass, but now is built on the new Styles
representation and is far more powerful than the first implementation. It safely
does nothing if you do not have a color-compatible systems (posix + tty
currently), but can be forced if need be. It is included with Plumbum, so you
don’t have to add a requirement for your scripts that is non-essential (as color
often is). It is integrated with plumbum.cli
, too. Also, I’ve managed to
accelerate the color selection algorithms about 8x, allowing near game-like
speeds. (see the fullcolor.py
example).
Note: The
plumbum.colors
library was written for the terminal and ANSI escape sequences. However, the following article was written in the IPython notebook, so it cannot show ANSI escapes. Due to the fact that theplumbum.colors
library uses a flexibleStyles
based representation, HTML is easy to implement as a Styles subclass and is available as htmlcolors (see docs for an example). With theplumbum.colorlib
IPython extension, IPython loads the%%to html
magic and makesplumbum.colorlib.htmlcolors
available ascolors
.htmlcolors
does not supportcolors.reset
(and therefore usingcolors
directly as a context manager, as well), but otherwise it is very similar. If we are constructing strings, we’ll need to remember to include HTML line breaks, so we’ll redefine print too.
%load_ext plumbum.colorlib
from functools import partial
print = partial(print, end='<br/>\n')
Plumbum.colors
Safe color manipulations made easy
This is a quick overview and tutorial on the plumbum.colors
library that is
being proposed for Plumbum. It allows you to do things like this:
%%to html
with colors.blue:
print("This is in Blue.")
print("But this is not.")
This is in Blue.
But this is not.
It works through the COLOR object, which gives you access to styles, and the
terminal colors through the foreground and background objects. You can wrap a
string with the |
operator:
%%to html
print(colors.red | "This is in red", '... but not this')
print("You can change the background too!" | colors.bg.light_yellow)
This is in red … but not this
You can change the background
too!
The color can go on either side of the string you are wrapping.
Styles are available, too:
%%to html
with colors.green:
print(colors.underline | "This is underlined", "and this is still green!")
This is
underlined and this is still green!
You can combine styles, and the result is still a valid style:
%%to html
mix = colors.bold & colors.italics & colors.red & colors.bg.light_green
print(mix | "This is a muddle of styles!")
with (colors.strikeout & colors.red):
print("Twin styles")
This is a
muddle of styles!
Twin
styles
All the major ANSI represetations are supported, include Basic (the first 8 colors), Simple (the first 16 colors), Full (256 colors using three parameter color codes), and True (24 bit color, using 5 parameter color codes). You can even find the closest color in a lower representation if you need to.
%%to html
print(colors.dark_blue | 'This is from the extended color set.')
print(colors['LIGHT_SEA_GREEN'] | 'And another one.')
print(colors.rgb(193,41,210) | 'This supports all colors!')
print(colors["#3AB227"] | 'Hex notation, too.')
This is from the extended color set.
And another one.
This
supports all colors!
Hex notation,
too.
The full list is on the plumbum.colors
ReadTheDocs page.
As a quick shortcut, you use .print
directly on color (.print_
if you are
using the classic print statement in Python 2):
%%to html
colors.orchid.print("This is in orchid.")
colors.bg.magenta.print("This is on a magenta background.")
This is in orchid.
This is on a magenta
background.
The colors can be iterated and sliced:
%%to html
for color in colors.fg[:16]:
print(color | "This is color:", color.fg.name.upper())
This is color: BLACK
This is color: RED
This
is color: GREEN
This is color:
YELLOW
This is color: BLUE
This is color: MAGENTA
This is color: CYAN
This
is color: LIGHT_GRAY
This is color:
DARK_GRAY
This is color: LIGHT_RED
This is color: LIGHT_GREEN
This is color: LIGHT_YELLOW
This is color: LIGHT_BLUE
This is color: LIGHT_MAGENTA
This is color: LIGHT_CYAN
This is color: WHITE
%%to html
for color in colors:
color.print("■", end=' ')
■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
Finally, you can also use []
notation to wrap a color (less convenient, but
similar to other methods):
%%to html
print(colors.blue['This is wrapped in blue'])
This is wrapped in blue
Unsafe color manipulations, too
Sometimes, you will find unsafe manipulations faster than wrapping every string.
This can be done with plumbum.colors
, too.
If you are planning unsafe manipulations, you can wrap your code in a context manager that restores color to your terminal. For example,
with colors:
...unsafe color operations...
All styles will be restored on leaving the manager. The color will automatically be reset when Python quits, as well, even on an exception, so this is not necessary, but is useful in local code.
Also, you can restore or set color instantly using the emergency restore from a terminal:
$ python -m plumbum.colorlib
This takes any string that you can use in colors, and without a string, it restores color.
The string representation of a color is the ANSI sequence that would produce the color. If you want to instantly write a color to the terminal, you can call the color without arguments. So, either of the following would change the color to blue without restoring it:
print(colors.blue, end="")
colors.blue()
To get the reset color, you can either use the .reset
property on a factory or
a style, or you can use ~
(inversion). So, this would be a manual color safe
wrapping from unsafe components:
%%to html
print("Before " + colors.red + "Middle" + ~colors.red + " After")
print("Before " + colors.blue + "Middle" + ~colors.fg + " After")
print("Before " + colors.green + "Middle" + colors.fg.reset + " After")
Before Middle After
Before
Middle After
Before
Middle After
Details of the Style Factories:
Let’s look at the contents of a colors.fg
or colors.bg
object:
{x for x in dir(colors.bg) if x[0] != "_"}
{'ansi',
'black',
'blue',
'cyan',
'dark_gray',
'full',
'green',
'hex',
'light_blue',
'light_cyan',
'light_gray',
'light_green',
'light_magenta',
'light_red',
'light_yellow',
'magenta',
'red',
'reset',
'rgb',
'simple',
'white',
'yellow'}
Notice that the extended colors are not listed, to make completion easier. Also, color access is not case sensitive and ignores underscores.
Since the colors
object looks like a fg
object, let’s only look at the
unique contents (.reset
has a different meaning for colors
, as it resets the
terminal completely instead of just the foreground color, so let’s remove it
from the fg
set):
fg = {x for x in dir(colors.bg) if x[0] != "_" and x != "reset"}
col = {x for x in dir(colors) if x[0] != "_"}
col - fg
{'bg',
'bold',
'code',
'contains_colors',
'do_nothing',
'em',
'extract',
'fatal',
'fg',
'filter',
'from_ansi',
'get_colors_from_string',
'highlight',
'info',
'italics',
'li',
'load_stylesheet',
'ol',
'reset',
'stdout',
'strikeout',
'success',
'title',
'underline',
'use_color',
'warn'}
Note that the properties are generated based on the attributes allowed for a style, so HTML has some slight differences here vs. ANSI.
Stylesheets
A recent addition to colors is stylesheets. Stylesheets allow you to use and create styles based on usage. The default sheet is the following:
default_styles = dict(
warn="fg red",
title="fg cyan underline bold",
fatal="fg red bold",
highlight="bg yellow",
info="fg blue",
success="fg green",
)
You can load a new sheet or a changed sheet with
colors.load_stylesheet(default_styles)
. The new and changed styles will be
accessible just like any normal color.
Bonus
The cell magic we’ve been using is actually a slight upgrade on the following example:
We are going to make a quick cell magic for IPython to capture output and render html from it. It’s really not hard to make a cell magic in IPython:
from io import StringIO
from IPython.display import display_html
from contextlib import redirect_stdout
from IPython.core.magic import register_cell_magic
@register_cell_magic
def output_html(line, cell):
"Captures stdout and renders it in the notebook as html."
out = StringIO()
with redirect_stdout(out):
exec(cell)
out.seek(0)
display_html(out.getvalue(), raw=True)
Let’s test this to make sure it works:
%%output_html
print("<p>Wow!</p>")
Wow!