Fabulous¶
Version: | 0.3.0 |
---|---|
Founder: | Justine Alexandra Roberts Tunney |
Copyright: | Copyright 2016 The Fabulous Authors. All rights reserved. |
License: | Apache 2.0 / OFL |
Support: | Python 2.6, 2.7, 3.3, 3.4, 3.5, and pypy |
Source: | github.com/jart/fabulous |
Fabulous is a Python library (and command line tools) designed to make the output of terminal applications look fabulous. Fabulous allows you to print colors, images, and stylized text to the console (without curses.) Fabulous also offers features to improve the usability of Python’s standard logging system.
Installation¶
The following prerequisites should be installed, but they are not mandatory. They help Fabulous run faster and make the full feature set available:
sudo apt-get install gcc python-imaging
Fabulous can be installed from CheeseShop:
sudo pip install fabulous
Fabulous can also be installed manually from the source archive:
wget https://github.com/jart/fabulous/releases/download/0.3.0/fabulous-0.3.0.tar.gz
tar -xvzf fabulous-0.3.0.tar.gz
cd fabulous-0.3.0
sudo python setup.py install
Once installed, run the demo:
fabulous-demo
Examples¶
Colors¶
4-bit colors and styles are standard and work almost everywhere. They are useful in helping make your program output easier to read:
from fabulous.color import bold, magenta, highlight_red
print bold(magenta('hello world'))
print highlight_red('DANGER WILL ROBINSON!')
print bold('hello') + ' ' + magenta(' world')
assert len(bold('test')) == 4
8-bit color works in most modern terminals, such as gnome-terminal and Terminal.app:
from fabulous import fg256, bg256
print fg256('#F0F', 'hello world')
print fg256('magenta', 'hello world')
Fancy Text¶
This is something neat you can use when you program starts up to display its name with style:
from fabulous import text
print text.Text("Fabulous!", color='#0099ff', shadow=True, skew=5)
Images¶
Fabulous lets you print images, which is more fun than useful. Fabulous’ unique method of printing images really shines when used with semi-transparent PNG files. When blending backgrounds, Fabulous assumes by default that your terminal has a black background. Don’t worry if your image is huge, it’ll be resized by default to fit your terminal:
from fabulous import utils, image
print image.Image("balls.png")
# adjust for a white background
utils.term.bgcolor = 'white'
print image.Image("balls.png")
Image printing may perform slowly depending on whether or not Fabulous is able
to compile ~/.xterm256.so
on the fly. This is a tiny library that makes
color quantization go much faster. The pure Python version of the algorithm is
really slow because it’s implemented as a brute force nearest neighbor over
Euclidean distance search. Although an O(1) version of this algorithm exists
with slightly less correctness. Your humble author simply hasn’t had the time
to implement it in this library.
If you like this image printing feature, then please check out hiptext which is a C++ program written by the same author as Fabulous. It offers a much richer version of this same functionality. It can even play videos in the terminal. Also be sure to check out rickrollrc.
Commands¶
fabulous-text¶
fabulous-image¶
fabulous-demo¶
Displays a demo showing what Fabulous can do.
fabulous-gotham¶
The fabulous-gotham command is a gothic poetry generator. It is a gimmick feature that uses a simple mad lib algorithm. It has no concept of meter or rhyme. Users wanting a proper poetry generator should consider poemy2 which uses markov chains and isledict. It’s also written by the same author as Fabulous.
fabulous-rotatingcube¶
The fabulous-rotatingcube command is another gimmick feature that animates a wireframe rotating cube in the terminal. It runs until you hit Ctrl+C.
Library¶
fabulous.color¶
The color module provides an object-oriented abstraction for stylized text inside the terminal. This includes things like bold text, blinking text, 4-bit ANSI colors and 8-bit xterm256 colors.
-
class
fabulous.color.
ColorString
(*items)¶ Abstract base class for stylized string-like objects.
Subclasses make it possible to compose stylized text:
>>> str(red("hello")) '\x1b[31mhello\x1b[39m' >>> str(bold(red("hello"))) '\x1b[1m\x1b[31mhello\x1b[39m\x1b[22m' >>> str(plain("hello ", bold("world"))) 'hello \x1b[1mworld\x1b[22m'
These objects also provide string length without taking into consideration the ANSI escape codes:
>>> len(red("hello")) 5 >>> len(str(red("hello"))) 15 >>> len(bold(red("hello"))) 5 >>> len(bold("hello ", red("world"))) 11
-
as_utf8
¶ A more readable way to say
unicode(color).encode('utf8')
-
-
class
fabulous.color.
ColorString256
(color, *items)¶ Base class for 256-color stylized string-like objects.
See the
fg256
,bg256
,highlight256
, andcomplement256
classes for more information.
-
class
fabulous.color.
bg256
(color, *items)¶ xterm256 background color wrapper
This class creates a string-like object that has an xterm256 color. The color is specified as a CSS color code, which is automatically quantized to the available set of xterm colors.
These colors are more dependable than the 4-bit colors, because 8-bit colors don’t get changed by the terminal theme. They will consistently be the requested color.
However it is worth noting that in Terminal.app on Mac OS, 8-bit background colors are ever so slightly different than their foreground equivalent. Therefore Terminal.app has effectively 512 colors.
Example usage:
from fabulous import bg256, plain print bg256('#F00', 'i have a red background!') print bg256('#FF0000', 'i have a red background!') print bg256('magenta', 'i have a', ' magenta background!') print plain('hello ', bg256('magenta', 'world'))
The ANSI escape codes look as follows:
>>> str(bg256('red', 'hello')) '\x1b[48;5;196mhello\x1b[49m'
-
class
fabulous.color.
black
(*items)¶ Black foreground text wrapper
This class creates a string-like object containing text with a black foreground.
Example usage:
from fabulous.color import black print black('i am black!') print plain('hello ', black('world'))
Text can be made dark grey by using
bold
:from fabulous.color import bold, black print bold(black('i am dark grey!'))
The ANSI escape codes are as follows:
>>> str(black("hello")) '\x1b[30mhello\x1b[39m'
-
class
fabulous.color.
black_bg
(*items)¶ Black background text wrapper
This class creates a string-like object containing text with a black background. On properly configured terminals, this will do nothing.
Example usage:
from fabulous.color import black_bg print black_bg('i have a black background!') print plain('hello ', black_bg('world'))
The ANSI escape codes are as follows:
>>> str(black_bg("hello")) '\x1b[40mhello\x1b[49m'
-
class
fabulous.color.
blink
(*items)¶ Blinking text wrapper
This class creates a string-like object containing blinking text. This is supported by SOME terminals, as documented in the terminal support section.
Example usage:
from fabulous.color import blink print blink('i am underlined!') print plain('hello ', blink('world'))
The ANSI escape codes are as follows:
>>> str(blink("hello")) '\x1b[5mhello\x1b[25m'
-
class
fabulous.color.
blue
(*items)¶ Blue foreground text wrapper
This class creates a string-like object containing text with a blue foreground.
Example usage:
from fabulous.color import blue print blue('i am dark blue!') print plain('hello ', blue('world'))
Text can be made sky blue by using
bold
:from fabulous.color import bold, blue print bold(blue('i am sky blue!'))
The ANSI escape codes are as follows:
>>> str(blue("hello")) '\x1b[34mhello\x1b[39m'
-
class
fabulous.color.
blue_bg
(*items)¶ Blue background text wrapper
This class creates a string-like object containing text with a blue background.
Example usage:
from fabulous.color import blue_bg print blue_bg('i have a blue background!') print plain('hello ', blue_bg('world'))
The ANSI escape codes are as follows:
>>> str(blue_bg("hello")) '\x1b[44mhello\x1b[49m'
-
class
fabulous.color.
bold
(*items)¶ Bold text wrapper
This class creates a string-like object containing bold or bright text. It also brightens the foreground and background colors. This is supported by all terminals that support ANSI color codes.
Example usage:
from fabulous.color import bold print bold('i am bold!') print plain('hello ', bold('world'))
The ANSI escape codes are as follows:
>>> str(bold("hello")) '\x1b[1mhello\x1b[22m'
-
fabulous.color.
complement
(color)¶ Calculates polar opposite of color
This isn’t guaranteed to look good >_> (especially with brighter, higher intensity colors.) This will be replaced with a formula that produces better looking colors in the future.
>>> complement('red') (0, 255, 76) >>> complement((0, 100, 175)) (175, 101, 0)
-
class
fabulous.color.
complement256
(color, *items)¶ Highlighted 8-bit color text
This class composes
bold
,flip
, andbg256
. Then it invokescomplement()
to supply the polar oppositefg256
color.This looks kind of hideous at the moment. We’re planning on finding a better formula for complementary colors in the future.
-
class
fabulous.color.
cyan
(*items)¶ Cyan foreground text wrapper
This class creates a string-like object containing text with a cyan foreground.
Example usage:
from fabulous.color import cyan print cyan('i am cyan!') print plain('hello ', cyan('world'))
Text can be made bright cyan by using
bold
:from fabulous.color import bold, cyan print bold(cyan('i am bright cyan!'))
The ANSI escape codes are as follows:
>>> str(cyan("hello")) '\x1b[36mhello\x1b[39m'
-
class
fabulous.color.
cyan_bg
(*items)¶ Cyan background text wrapper
This class creates a string-like object containing text with a cyan background.
Example usage:
from fabulous.color import cyan_bg print cyan_bg('i have a cyan background!') print plain('hello ', cyan_bg('world'))
The ANSI escape codes are as follows:
>>> str(cyan_bg("hello")) '\x1b[46mhello\x1b[49m'
-
fabulous.color.
esc
(*codes)¶ Produces an ANSI escape code string from a list of integers
This is a low level function that is abstracted by the other functions and classes in this module.
-
class
fabulous.color.
fg256
(color, *items)¶ xterm256 foreground color wrapper
This class creates a string-like object that has an xterm256 color. The color is specified as a CSS color code, which is automatically quantized to the available set of xterm colors.
These colors are more dependable than the 4-bit colors, because 8-bit colors don’t get changed by the terminal theme. They will consistently be the requested color, which is calculated using a simple math formula.
However it is worth noting that in Terminal.app on Mac OS, 8-bit colors appear to be designed rather than formulaic, so they look much nicer.
Example usage:
from fabulous import fg256, plain print fg256('#F00', 'i am red!') print fg256('#FF0000', 'i am red!') print fg256('magenta', 'i am', ' magenta!') print plain('hello ', fg256('magenta', 'world'))
The ANSI escape codes look as follows:
>>> str(fg256('red', 'hello')) '\x1b[38;5;196mhello\x1b[39m'
-
class
fabulous.color.
flip
(*items)¶ Flips background and foreground colors
For example:
from fabulous.color import flip, red print flip(red('hello'))
Is equivalent to the following on a black terminal:
from fabulous.color import black, red_bg print red_bg(black('hello'))
The ANSI escape codes are as follows:
>>> str(flip("hello")) '\x1b[7mhello\x1b[27m'
-
class
fabulous.color.
green
(*items)¶ Green foreground text wrapper
This class creates a string-like object containing text with a green foreground.
Example usage:
from fabulous.color import green print green('i am green!') print plain('hello ', green('world'))
Text can be made bright green by using
bold
:from fabulous.color import bold, green print bold(green('i am bright green!'))
The ANSI escape codes are as follows:
>>> str(green("hello")) '\x1b[32mhello\x1b[39m'
-
class
fabulous.color.
green_bg
(*items)¶ Green background text wrapper
This class creates a string-like object containing text with a green background.
Example usage:
from fabulous.color import green_bg print green_bg('i have a green background!') print plain('hello ', green_bg('world'))
The ANSI escape codes are as follows:
>>> str(green_bg("hello")) '\x1b[42mhello\x1b[49m'
-
fabulous.color.
h1
(title, line=u'\u203e')¶ Prints bold text with line beneath it spanning width of terminal
-
class
fabulous.color.
highlight256
(color, *items)¶ Highlighted 8-bit color text
-
class
fabulous.color.
highlight_black
(*items)¶ Dark grey highlight text wrapper
-
class
fabulous.color.
highlight_blue
(*items)¶ Blue highlight text wrapper
-
class
fabulous.color.
highlight_cyan
(*items)¶ Cyan highlight text wrapper
-
class
fabulous.color.
highlight_green
(*items)¶ Green highlight text wrapper
-
class
fabulous.color.
highlight_magenta
(*items)¶ Hot pink highlight text wrapper
-
class
fabulous.color.
highlight_red
(*items)¶ Red highlight text wrapper
-
class
fabulous.color.
highlight_white
(*items)¶ White highlight text wrapper
-
class
fabulous.color.
highlight_yellow
(*items)¶ Yellow highlight text wrapper
-
class
fabulous.color.
italic
(*items)¶ Italic text wrapper
This class creates a string-like object containing italic text, which is supported by almost no terminals.
The ANSI escape codes are as follows:
>>> str(italic("hello")) '\x1b[3mhello\x1b[23m'
-
class
fabulous.color.
magenta
(*items)¶ Purple/magenta foreground text wrapper
This class creates a string-like object containing text with a magenta foreground. Although in many terminals, it’s going to look more purple.
Example usage:
from fabulous.color import magenta print magenta('i am magenta purplish!') print plain('hello ', magenta('world'))
Text can be made hot pink by using
bold
:from fabulous.color import bold, magenta print bold(magenta('i am hot pink!'))
The ANSI escape codes are as follows:
>>> str(magenta("hello")) '\x1b[35mhello\x1b[39m'
-
class
fabulous.color.
magenta_bg
(*items)¶ Magenta background text wrapper
This class creates a string-like object containing text with a magenta background.
Example usage:
from fabulous.color import magenta_bg print magenta_bg('i have a magenta background!') print plain('hello ', magenta_bg('world'))
The ANSI escape codes are as follows:
>>> str(magenta_bg("hello")) '\x1b[45mhello\x1b[49m'
-
fabulous.color.
parse_color
(color)¶ Turns a color into an (r, g, b) tuple
>>> parse_color('white') (255, 255, 255) >>> parse_color('#ff0000') (255, 0, 0) >>> parse_color('#f00') (255, 0, 0) >>> parse_color((255, 0, 0)) (255, 0, 0) >>> from fabulous import grapefruit >>> parse_color(grapefruit.Color((0.0, 1.0, 0.0))) (0, 255, 0)
-
class
fabulous.color.
plain
(*items)¶ Plain text wrapper
This class is useful for concatenating plain strings with
ColorString
objects. For example:from fabulous.color import plain >>> len(plain("hello ", bold("kitty"))) 11
-
class
fabulous.color.
red
(*items)¶ Red foreground text wrapper
This class creates a string-like object containing text with a red foreground.
Example usage:
from fabulous.color import red print red('i am red!') print plain('hello ', red('world'))
Text can be made bright red by using
bold
:from fabulous.color import bold, red print bold(red('i am bright red!'))
The ANSI escape codes are as follows:
>>> str(red("hello")) '\x1b[31mhello\x1b[39m'
-
class
fabulous.color.
red_bg
(*items)¶ Red background text wrapper
This class creates a string-like object containing text with a red background.
Example usage:
from fabulous.color import red_bg print red_bg('i have a red background!') print plain('hello ', red_bg('world'))
The ANSI escape codes are as follows:
>>> str(red_bg("hello")) '\x1b[41mhello\x1b[49m'
-
fabulous.color.
section
(title, bar=u'\u203e', strm=<open file '<stdout>', mode 'w'>)¶ Helper function for testing demo routines
-
class
fabulous.color.
strike
(*items)¶ Strike-through text wrapper
This class creates a string-like object containing strike-through text, which is supported by very few terminals.
Example usage:
from fabulous.color import strike print strike('i am stricken!') print plain('hello ', strike('world'))
The ANSI escape codes are as follows:
>>> str(strike("hello")) '\x1b[9mhello\x1b[29m'
-
class
fabulous.color.
underline
(*items)¶ Underline text wrapper
This class creates a string-like object containing underline text. This is supported by SOME terminals, as documented in the terminal support section.
Example usage:
from fabulous.color import underline print underline('i am underlined!') print plain('hello ', underline('world'))
The ANSI escape codes are as follows:
>>> str(underline("hello")) '\x1b[4mhello\x1b[24m'
-
class
fabulous.color.
underline2
(*items)¶ Alternative underline text wrapper
See also:
underline
.The ANSI escape codes are as follows:
>>> str(underline2("hello")) '\x1b[21mhello\x1b[24m'
-
class
fabulous.color.
white
(*items)¶ White foreground text wrapper
This class creates a string-like object containing text with a light grey foreground.
Example usage:
from fabulous.color import white print white('i am light grey!') print plain('hello ', white('world'))
Text can be made true white by using
bold
:from fabulous.color import bold, white print bold(white('i am bold white!'))
The ANSI escape codes are as follows:
>>> str(white("hello")) '\x1b[37mhello\x1b[39m'
-
class
fabulous.color.
white_bg
(*items)¶ White background text wrapper
This class creates a string-like object containing text with a white background.
Example usage:
from fabulous.color import white_bg print white_bg('i have a white background!') print plain('hello ', white_bg('world'))
The ANSI escape codes are as follows:
>>> str(white_bg("hello")) '\x1b[47mhello\x1b[49m'
-
class
fabulous.color.
yellow
(*items)¶ Yellow foreground text wrapper
This class creates a string-like object containing text with a “yellow” foreground, which in many terminals is actually going to look more brownish.
Example usage:
from fabulous.color import yellow print yellow('i am yellow brownish!') print plain('hello ', yellow('world'))
Text can be made true bright yellow by using
bold
:from fabulous.color import bold, yellow print bold(yellow('i am bright yellow!'))
The ANSI escape codes are as follows:
>>> str(yellow("hello")) '\x1b[33mhello\x1b[39m'
-
class
fabulous.color.
yellow_bg
(*items)¶ Yellow background text wrapper
This class creates a string-like object containing text with a yellow background.
Example usage:
from fabulous.color import yellow_bg print yellow_bg('i have a yellow background!') print plain('hello ', yellow_bg('world'))
The ANSI escape codes are as follows:
>>> str(yellow_bg("hello")) '\x1b[43mhello\x1b[49m'
fabulous.xterm256¶
The xterm256 module provides support for the 256 colors supported by xterm as well as quantizing 24-bit RGB color to xterm color ids.
Color quantization may perform slowly depending on whether or not Fabulous
is able to compile ~/.xterm256.so
on the fly. This is a tiny library
that makes color quantization go much faster. The pure Python version of the
algorithm is really slow because it’s implemented as a brute force nearest
neighbor over Euclidean distance search. Although an O(1) version of this
algorithm exists with slightly less correctness. Your humble author simply
hasn’t had the time to implement it in this library.
-
fabulous.xterm256.
compile_speedup
()¶ Tries to compile/link the C version of this module
Like it really makes a huge difference. With a little bit of luck this should just work for you.
You need:
- Python >= 2.5 for ctypes library
-
fabulous.xterm256.
rgb_to_xterm
(r, g, b)¶ Quantize RGB values to an xterm 256-color ID
This works by envisioning the RGB values for all 256 xterm colors as 3D euclidean space and brute-force searching for the nearest neighbor.
This is very slow. If you’re very lucky,
compile_speedup()
will replace this function automatically with routines in _xterm256.c.
-
fabulous.xterm256.
xterm_to_rgb
(xcolor)¶ Convert xterm Color ID to an RGB value
All 256 values are precalculated and stored in
COLOR_TABLE
fabulous.text¶
The text module makes it possible to print TrueType text to the terminal. This functionality is available on the command line:
jart@compy:~$ fabulous-text --help
jart@compy:~$ fabulous-text --skew=5 --shadow 'Fabulous!'
jart@compy:~$ python -m fabulous.text --help
Or as a Python library:
from fabulous import text
print text.Text("Fabulous!", color='#0099ff', shadow=True, skew=5)
To make things simple, Fabulous bundles the following Google Noto Fonts which look good and are guaranteed to work no matter what:
- NotoSans-Bold
- NotoEmoji-Regular
For other fonts, Fabulous will do its best to figure out where they are
stored. If Fabulous has trouble finding your font, try using an absolute
path with the extension. It’s also possible to put the font in the
~/.fonts
directory and then running fc-cache -fv ~/.fonts
.
You can run fabulous-text --list
to see what fonts are available.
-
exception
fabulous.text.
FontNotFound
¶ I get raised when the font-searching hueristics fail
This class extends the standard
ValueError
exception so you don’t have to import me if you don’t want to.
-
class
fabulous.text.
Text
(text, fsize=23, color='#0099ff', shadow=False, skew=None, font='NotoSans-Bold')¶ Renders TrueType Text to Terminal
I’m a sub-class of
fabulous.image.Image
. My job is limited to simply getting things ready. I do this by:- Turning your text into an RGB-Alpha bitmap image using
PIL
- Applying way cool effects (if you choose to enable them)
For example:
>>> assert Text("Fabulous", shadow=True, skew=5) >>> txt = Text("lorem ipsum", font="NotoSans-Bold") >>> len(str(txt)) > 0 True >>> txt = Text(u"😃", font="NotoSans-Bold") >>> len(str(txt)) > 0 True
Parameters: - text – The text you want to display as a string.
- fsize – The font size in points. This obviously end up looking much larger because in fabulous a single character is treated as one horizontal pixel and two vertical pixels.
- color – The color (specified as you would in HTML/CSS) of
your text. For example Red could be specified as
follows:
red
,#00F
or#0000FF
. - shadow – If true, render a simple drop-shadow beneath text. The Fabulous logo uses this feature.
- skew – Skew size in pixels. This applies an affine transform to shift the top-most pixels to the right. The Fabulous logo uses a five pixel skew.
- font – The TrueType font you want. If this is not an absolute path, Fabulous will search for your font by globbing the specified name in various directories.
- Turning your text into an RGB-Alpha bitmap image using
-
fabulous.text.
get_font_files
()¶ Returns a list of all font files we could find
Returned as a list of dir/files tuples:
get_font_files() -> {'FontName': '/abs/FontName.ttf', ...]
For example:
>>> fonts = get_font_files() >>> 'NotoSans-Bold' in fonts True >>> fonts['NotoSans-Bold'].endswith('/NotoSans-Bold.ttf') True
-
fabulous.text.
main
()¶ Main function for fabulous-text.
-
fabulous.text.
resolve_font
(name)¶ Turns font names into absolute filenames
This is case sensitive. The extension should be omitted.
For example:
>>> path = resolve_font('NotoSans-Bold') >>> fontdir = os.path.join(os.path.dirname(__file__), 'fonts') >>> noto_path = os.path.join(fontdir, 'NotoSans-Bold.ttf') >>> noto_path = os.path.abspath(noto_path) >>> assert path == noto_path
Absolute paths are allowed:
>>> resolve_font(noto_path) == noto_path True
Raises
FontNotFound
on failure:>>> try: ... resolve_font('blahahaha') ... assert False ... except FontNotFound: ... pass
fabulous.image¶
The image module makes it possible to print images to the terminal.
This module is available as a command line tool:
jart@compy:~$ fabulous-image foo.png
jart@compy:~$ python -m fabulous.image foo.png
-
class
fabulous.image.
Image
(path, width=None)¶ Printing image files to a terminal
I use
PIL
to turn your image file into a bitmap, resize it so it’ll fit inside your terminal, and implement methods so I can behave like a string or iterable.When resizing, I’ll assume that a single character on the terminal display is one pixel wide and two pixels tall. For most fonts this is the best way to preserve the aspect ratio of your image.
All colors are are quantized by
fabulous.xterm256
to the 256 colors supported by modern terminals. When quantizing semi-transparant pixels (common in text or PNG files) I’ll askTerminalInfo
for the background color I should use to solidify the color. Fully transparent pixels will be rendered as a blank space without color so we don’t need to mix in a background color.I also put a lot of work into optimizing the output line-by-line so it needs as few ANSI escape sequences as possible. If your terminal is kinda slow, you’re gonna want to buy me a drink ;) You can use
DebugImage
to visualize these optimizations.The generated output will only include spaces with different background colors. In the future routines will be provided to overlay text on top of these images.
-
convert
()¶ Yields xterm color codes for each pixel in image
-
reduce
(colors)¶ Converts color codes into optimized text
This optimizer works by merging adjacent colors so we don’t have to repeat the same escape codes for each pixel. There is no loss of information.
Parameters: colors – Iterable yielding an xterm color code for each pixel, None to indicate a transparent pixel, or 'EOL'
to indicate th end of a line.Returns: Yields lines of optimized text.
-
resize
(width=None)¶ Resizes image to fit inside terminal
Called by the constructor automatically.
-
size
¶ Returns size of image
-
-
fabulous.image.
main
()¶ Main function for fabulous-image.
fabulous.logs¶
Utilities for transient logging.
This is very useful tool for monitoring what your Python scripts are doing. It allows you to have full verbosity without drowning out important error messages:
import time, logging
from fabulous import logs
logs.basicConfig(level='WARNING')
for n in range(20):
logging.debug("verbose stuff you don't care about")
time.sleep(0.1)
logging.warning("something bad happened!")
for n in range(20):
logging.debug("verbose stuff you don't care about")
time.sleep(0.1)
-
class
fabulous.logs.
TransientStreamHandler
(strm=<open file '<stderr>', mode 'w'>, level=30)¶ Standard Python logging Handler for Transient Console Logging
Logging transiently means that verbose logging messages like DEBUG will only appear on the last line of your terminal for a short period of time and important messages like WARNING will scroll like normal text.
This allows you to log lots of messages without the important stuff getting drowned out.
This module integrates with the standard Python logging module.
-
fabulous.logs.
basicConfig
(level=30, transient_level=0)¶ Shortcut for setting up transient logging
I am a replica of
logging.basicConfig
which installs a transient logging handler to stderr.
fabulous.widget¶
Widget library using terminate.
-
class
fabulous.widget.
ProgressBar
(title=None)¶ - A 3-line progress bar, which looks like::
- title
- 39% [================>—————————-]
- message
p = ProgressBar(‘spam’) # create bar p.update(0, ‘starting spam’) # start printing it out p.update(50, ‘spam almost ready’) # progress p.update(100, ‘spam complete’)
-
clear
()¶
-
get_bar
(percent)¶
-
get_message
()¶ returns None or string
-
get_title
()¶
-
set_message
(message=None)¶
-
set_title
(title=None)¶
-
update
(percent, message=None, test=False)¶
-
class
fabulous.widget.
TimedProgressBar
(title=None)¶ - A 3-line progress bar, which looks like::
- title
- 39% [================>—————————-] ETA mm:ss
- message
p = ProgressBar(‘spam’) # create bar p.update(0, ‘starting spam’) # start printing it out p.update(50, ‘spam almost ready’) # progress p.update(100, ‘spam complete’)
-
BAR_FORMAT
= {'padding': 2, 'text': ' %3d%% [%s\x1b[2m%s\x1b[0m]', 'length': 13}¶ ETA 12:23
fabulous.term¶
Terminal abstraction layer.
Provides standard capabilites to a variety of terminals. Support information is being worked on.
import os
os.stdout.write('spam' +
display('bright','yellow','white') +
'eggs' +
display('default') + os.linesep)
Warning: on IPython setting sys.stdout to stdout will break readline
Caveat: Failure to flush after ouput can cause weird ordering behaviour when writing to stdout and stderr simutaniously. This should fix the worst of it, but application developers should be warned not to rely on the state of things between call between one method call and another
-
fabulous.term.
display
(codes=[], fg=None, bg=None)¶ Returns an ANSI display code. This is useful when writing to an Term
- codes
- A list containing strings. The strings should one of the keys in
Magic.DISPLAY
. It can also be just a single string. - fg, bg
- A string. Explicitly for setting the foreground or background. Use
one of the keys in
Magic.COLORS
.
# give bright blue foreground and white background with underline display(('bright','underline'),'blue','white') # gives a blue foreground display(fg='blue') # resets the color to the default. display('default')
Avoid using black or white. Depending on the situation the default background/foreground is normally black or white, but it’s hard to tell which. Bare terminals are normally white on black, but virtual terminals run from X or another GUI system are often black on white. This can lead to unpredicatble results. If you want reversed colours, use the ‘reverse’ code, and if you want to set the colors back to their original colors, use the ‘default’ code.
Also, be prudent with your use of ‘hidden’ and ‘blink’. Several terminals do not support them (and for good reason too), they can be really annoying and make reading difficult.
-
class
fabulous.term.
Term
(stream)¶ A file-like object which also supports terminal features.
This is a base class for dumb terminals. It supports almost nothing.
-
bell
()¶ Causes the computer to beep
Use sparingly, it is mainly to alert the user if something potentialy bad may be happening.
-
clear
(scope='screen')¶ clears part of the screen
The valid values for scope are:
- right
- Clears a single space directly to the right.
- left
- Clears a single space directly to the left.
- line
- Clears the current line.
- screen
- Clears the whole screen.
- beginning of line
- Clears from the current position to the beginning of the line
- end of line
- Clears from the current position to the end of the line
- end of screen
- Clears from the current position to the end of the screen
N.b. this is not the same as deleting. After a place is cleared it should still be there, but with nothing in it. Also, this should not change the position of the cursor.
-
display
(codes=[], fg=None, bg=None)¶ Not for public consumption (yet)
Just use display() and stdout.write() for now.
run this at the beginning:
(codes, fg, bg) = Magic.displayformat(codes, fg, bg)
-
fileno
()¶ Returns the stream’s file descriptor as an integer
-
flush
()¶ Ensure the text is ouput to the screen.
The write() method will do this automatically, so only use this when using self.stream.write().
-
get_size
()¶ Get the width and height of the terminal.
Returns either a tuple of two integers or None. If two integers are returned, the first one is the number of columns (or width) and the second value is the number of lines (or height). If None is returned, then the terminal does not support this feature. If you still need to have a value to fall back on (75, 25) is a fairly descent fallback.
-
getch
()¶ Don’t use this yet
It doesn’t belong here but I haven’t yet thought about a proper way to implement this feature and the features that will depend on it.
-
isatty
()¶ Returns True if the terminal is a terminal
This should always be True. If it’s not somebody is being rather nauty.
-
move
(place, distance=1)¶ Move cursor position
The valid values for place are:
- up
- Move up a line.
- down
- Move to the next line. This also puts you at the beginning of the line.
- left
- Move one place to the left.
- right
- Move one place to the right.
- beginning of line
- Move to the beginning of the current line.
- beginning of screen
- Move to the beginning of the screen.
-
raw_input
(prompt)¶ Don’t use this yet
It doesn’t belong here but I haven’t yet thought about a proper way to implement this feature and the features that will depend on it.
-
set_title
(name)¶ Sets the title of the terminal
-
write
(text)¶ Parses text and prints proper output to the terminal
This method will extract escape codes from the text and handle them as well as possible for whichever platform is being used. At the moment only the display escape codes are supported.
-
writelines
(sequence_of_strings)¶ Write out a sequence of strings
Note that newlines are not added. The sequence may be any iterable object producing strings. This is equivalent to calling write() for each string.
-
-
class
fabulous.term.
WinTerm
(stream)¶ Windows version of terminal control
This class should not be used by itself, use either Win32Terminal or WinCTypesTerminal classes that subclasses of this class.
This class makes extensive use of the Windows API
The official documentation for the API is on MSDN (look for ‘console functions’)
-
clear
(scope='screen')¶ see doc in Term class
According to http://support.microsoft.com/kb/99261 the best way to clear the console is to write out empty spaces
-
display
(codes=[], fg=None, bg=None)¶ Displays codes using Windows kernel calls
-
get_size
()¶ see doc in Term class
-
getch
()¶ Don’t use this yet
It doesn’t belong here but I haven’t yet thought about a proper way to implement this feature and the features that will depend on it.
-
move
(place, distance=1)¶ see doc in Term class
-
-
class
fabulous.term.
Win32Term
(stream)¶ PyWin32 version of Windows terminal control.
Uses the PyWin32 Libraries <http://sourceforge.net/projects/pywin32/>.
ActiveState has good documentation for them:
Main page: http://aspn.activestate.com/ASPN/docs/ActivePython/2.4/pywin32/PyWin32.html Console related objects and methods: http://aspn.activestate.com/ASPN/docs/ActivePython/2.4/pywin32/PyConsoleScreenBuffer.html
-
class
fabulous.term.
WinCTypesTerm
(stream)¶ CTypes version of Windows terminal control.
It requires the CTypes libraries <http://sourceforge.net/projects/ctypes/>
As of Python 2.5, CTypes is included in Python by default. User’s of previous version of Python will have to install it if they what to use this.
fabulous.rlcomplete¶
Readline related stuff.
-
class
fabulous.rlcomplete.
Completer
¶ A base class for completers.
Child classes should implement the completelist method.
-
complete
(text, state)¶ The actual completion method
This method is not meant to be overridden. Override the completelist method instead. It will make your life much easier.
For more detail see documentation for readline.set_completer
-
completelist
(text)¶ Returns a list.
The list contains a series of strings which are the suggestions for the given string
text
. It is valid to have no suggestions (empty list returned).
-
-
class
fabulous.rlcomplete.
ListCompleter
(words, ignorecase)¶ A class that does completion based on a predefined list.
-
class
fabulous.rlcomplete.
PathCompleter
¶ Does completion based on file paths.
-
completelist
(text)¶ Return a list of potential matches for completion
n.b. you want to complete to a file in the current working directory that starts with a ~, use ./~ when typing in. Paths that start with ~ are magical and specify users’ home paths
-
static
matchuserhome
(prefix)¶ To find matches that start with prefix.
For example, if prefix = ‘~user’ this returns list of possible matches in form of [‘~userspam’,’~usereggs’] etc.
matchuserdir(‘~’) returns all users
-
fabulous.gotham¶
This is a gimmick feature that generates silly gothic poetry.
This uses a simple mad lib algorithm. It has no concept of meter or rhyme. If you want a proper poetry generator, check out poemy2 which uses markov chains and isledict. It’s written by the same author as Fabulous.
This module can be run as a command line tool:
jart@compy:~$ fabulous-gotham
jart@compy:~$ python -m fabulous.gotham
-
fabulous.gotham.
lorem_gotham
()¶ Cheesy Gothic Poetry Generator
Uses Python generators to yield eternal angst.
When you need to generate random verbiage to test your code or typographic design, let’s face it... Lorem Ipsum and “the quick brown fox” are old and boring!
What you need is something with flavor, the kind of thing a depressed teenager with a lot of black makeup would write.
-
fabulous.gotham.
lorem_gotham_title
()¶ Names your poem
-
fabulous.gotham.
main
()¶ I provide a command-line interface for this module
fabulous.rotating_cube¶
Command for animating a wireframe rotating cube in the terminal.
-
class
fabulous.rotating_cube.
Frame
¶ Canvas object for drawing a frame to be printed
-
fabulous.rotating_cube.
rotating_cube
(degree_change=3, frame_rate=3)¶ Rotating cube program
How it works:
- Create two imaginary ellipses
- Sized to fit in the top third and bottom third of screen
- Create four imaginary points on each ellipse
- Make those points the top and bottom corners of your cube
- Connect the lines and render
- Rotate the points on the ellipses and repeat
fabulous.debug¶
The debug module provides the ability to print images as ASCII. It isn’t a good ASCII representation like cacalib. This module is mostly intended for debugging purposes (hence the name.)
-
class
fabulous.debug.
DebugImage
(path, width=None)¶ Visualize optimization techniques used by
Image
-
fabulous.debug.
main
()¶ I provide a command-line interface for this module
fabulous.utils¶
Miscellaneous utilities for Fabulous.
-
class
fabulous.utils.
TerminalInfo
(bgcolor='black')¶ Quick and easy access to some terminal information
I’ll tell you the terminal width/height and it’s background color.
You don’t need to use me directly. Just access the global
term
instance:>>> assert term.width > 0 >>> assert term.height > 0
It’s important to know the background color when rendering PNG images with semi-transparency. Because there’s no way to detect this, black will be the default:
>>> term.bgcolor (0.0, 0.0, 0.0, 1.0) >>> from fabulous import grapefruit >>> isinstance(term.bgcolor, grapefruit.Color) True
If you use a white terminal, you’ll need to manually change this:
>>> term.bgcolor = 'white' >>> term.bgcolor (1.0, 1.0, 1.0, 1.0) >>> term.bgcolor = grapefruit.Color.NewFromRgb(0.0, 0.0, 0.0, 1.0) >>> term.bgcolor (0.0, 0.0, 0.0, 1.0)
-
dimensions
¶ Returns terminal dimensions
Don’t save this information for long periods of time because the user might resize their terminal.
Returns: Returns (width, height)
. If there’s no terminal to be found, we’ll just return(79, 40)
.
-
height
¶ Returns height of terminal in lines
-
termfd
¶ Returns file descriptor number of terminal
This will look at all three standard i/o file descriptors and return whichever one is actually a TTY in case you’re redirecting i/o through pipes.
-
width
¶ Returns width of terminal in characters
-
-
fabulous.utils.
memoize
(function)¶ A very simple memoize decorator to optimize pure-ish functions
Don’t use this unless you’ve examined the code and see the potential risks.
-
fabulous.utils.
pil_check
()¶ Check for PIL library, printing friendly error if not found
We need PIL for the
fabulous.text
andfabulous.image
modules to work. Because PIL can be very tricky to install, it’s not listed in thesetup.py
requirements list.
Terminal Support¶
Supported Terminals¶
Terminal | default | bright | dim | underline | blink | reverse | hidden |
---|---|---|---|---|---|---|---|
xterm | yes | yes | yes | yes | yes | yes | yes |
linux | yes | yes | yes | bright | yes | yes | no |
rxvt | yes | yes | no | yes | bright | yes | no |
Windows [0] | yes | yes | yes | no | no | yes | yes |
PuTTY [1] | yes | yes | no | yes | [2] | yes | no |
Cygwin SSH [3] | yes | yes | no | [4] | [4] | [2] | yes |
Currently unsupported, but should support¶
Terminal | default | bright | dim | underline | blink | reverse | hidden |
---|---|---|---|---|---|---|---|
dtterm | yes | yes | yes | yes | reverse | yes | yes |
teraterm | yes | reverse | no | yes | rev/red | yes | no |
aixterm | kinda | normal | no | yes | no | yes | yes |
Mac Terminal | yes | yes | no | yes | yes | yes | yes |
Unsupported and will not support¶
- Windows Telnet
- It thinks it supports ANSI control, but it’s so horribly
buggy its best to ignore it all together. (
TERM = ansi
)
[0] | The default windows terminal, cmd.exe does not set the TERM variable, so
detection is done by checking if the string 'win32' is in sys.platform . This
This method has some limitations, particularly with remote terminal. But if you’re
allowing remote access to a Windows computer you probably have bigger problems. |
[1] | Putty has the TERM variable set to xterm by default |
[2] | (1, 2) Makes background bright |
[3] | Cygwin’s SSH support’s ANSI, but the regular terminal does not, check for
win32 first, then check for cygwin. That should give us the cases when
cygwin is used through SSH or telnet or something. (TERM = cygwin ) |
[4] | (1, 2) Sets foreground color to cyan |
Alternatives¶
Here’s how Fabulous compares to other similar libraries:
- fabulous: Licensed Apache 2.0. Focuses on delivering useful features in the simplest, most user-friendly way possible (without a repulsive name.) Written in pure-python but will attempt to auto-magically compile/link a speedup library. ~5,000 lines of code.
- libcaca: WTFPL. This is the established and respected standard for doing totally insane things with ascii art (ever wanted to watch a movie on the command line?) Weighing in at ~72k lines of C, this project is a monster. It uses an older, more complex text/dithering-based rendering method. Compared to fabulous, some images look better, some worse. I found the docs somewhat difficult to follow and couldn’t find support for transparency or 256-colors.
- asciiporn: GPL. Similar to libcaca but has an interesting feature for drawing math graphs to the terminal... Needs to compile C code, requires numpy/python2.6, and I couldn’t get the darn thing to work. Aprox 17k lines of code.
- pygments: BSD. Has excellent support for terminal syntax highlighting.
- termcolor: GPL. Only supports 4-bit ANSI colors.
License¶
Fabulous code and documentation are licensed Apache 2.0:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The bundled Google Noto Fonts are licensed under the SIL Open Font License, Version 1.1: