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 algorithims 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.")
It works through the COLOR object, which gives you access to styles, and the terminal colors through the forground 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)
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!")
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")
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.')
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 statment in Python 2):
%%to html
colors.orchid.print("This is in orchid.")
colors.bg.magenta.print("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())
%%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'])
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")
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] != '_'}
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 completly 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
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 accessable 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>")
No comments:
Post a Comment