FmtStr

FmtStr is a string with each character colored and styled in ways representable by ANSI escape codes.

FmtStr - Example

>>> from curtsies import fmtstr
>>> red_on_blue = fmtstr(u'hello', fg='red', bg='blue')
>>> from curtsies.fmtfuncs import *
>>> blue_on_red = blue(on_red(u'there'))
>>> bang = bold(underline(green(u'!')))
>>> full = red_on_blue + blue_on_red + bang
>>> str(full)
'\x1b[31m\x1b[44mhello\x1b[0m\x1b[0m\x1b[34m\x1b[41mthere\x1b[0m\x1b[0m\x1b[4m\x1b[32m\x1b[1m!\x1b[0m\x1b[0m\x1b[0m'
>>> print(full)
hellothere!

We start here with such a complicated example because it you only need something simple like:

>>> from curtsies.fmtfuncs import *
>>> print(blue(bold(u'Deep blue sea')))
Deep blue sea

then another library may be a better fit than Curtsies. Unlike other libraries, Curtsies allows these colored strings to be further manipulated after they are created.

FmtStr - Rationale

If all you need is to print colored text, many other libraries also make ANSI escape codes easy to use.

  • Blessings (pip install blessings) As of version 0.1.0, Curtsies uses Blessings for terminal capabilities other than colored output.
  • termcolor (pip install termcolor)
  • Clint (pip install clint)
  • colors (pip install colors)

In all of the libraries listed above, the expression blue('hi') + ' ' + green('there) or equivalent evaluates to a Python string, not a colored string object. If all you plan to do with this string is print it, this is great. But, if you need to do more formatting with this colored string later, the length will be something like 29 instead of 9; structured formatting information is lost. Methods like center and ljust won’t properly format the string for display.

>>> import blessings
>>> term = blessings.Terminal()
>>> message = term.red_on_green('Red on green?') + ' ' + term.yellow('Ick!')
>>> len(message)
41 # ?!
>>> message.center(50)
u'    \x1b[31m\x1b[42mRed on green?\x1b[m\x0f \x1b[33mIck!\x1b[m\x0f     '

FmtStr objects can be combined and composited to create more complicated FmtStr objects, useful for building flashy terminal interfaces with overlapping windows/widgets that can change size and depend on each other’s sizes. One FmtStr can have several kinds of formatting applied to different parts of it.

>>> from curtsies.fmtfuncs import *
>>> blue('asdf') + on_red('adsf')
blue("asdf")+on_red("adsf")

FmtStr - Using

A FmtStr can be sliced to produce a new FmtStr objects:

>>> from curtsies.fmtfuncs import *
>>> (blue('asdf') + on_red('adsf'))[3:7]
blue("f")+on_red("ads")

FmtStr are immutable - but you can create new ones with splice():

>>> from curtsies.fmtfuncs import *
>>> f = blue('hey there') + on_red(' Tom!')
>>> g.splice('ot', 1, 3)
>>> g
blue("h")+"ot"+blue(" there")+on_red(" Tom!")
>>> f.splice('something longer', 2)
blue("h")+"something longer"+blue("ot")+blue(" there")+on_red(" Tom!")

FmtStr greedily absorb strings, but no formatting is applied to this added text:

>>> from curtsies.fmtfuncs import *
>>> f = blue("The story so far:") + "In the beginning..."
>>> type(f)
<class curtsies.fmtstr.FmtStr>
>>> f
blue("The story so far:")+"In the beginning..."

It’s easy to turn ANSI terminal formatted strings into FmtStr:

>>> from curtsies.fmtfuncs import *
>>> from curtsies import FmtStr
>>> s = str(blue('tom'))
>>> s
'\x1b[34mtom\x1b[39m'
>>> FmtStr.from_str(str(blue('tom')))
blue("tom")

FmtStr - Using str methods

All sorts of string methods can be used on a FmtStr, so you can often use FmtStr objects where you had strings in your program before:

>>> from curtsies.fmtfuncs import *
>>> f = blue(underline('As you like it'))
>>> len(f)
14
>>> f == underline(blue('As you like it')) + red('')
True
>>> blue(', ').join(['a', red('b')])
"a"+blue(", ")+red("b")

If FmtStr doesn’t implement a method, it tries its best to use the string method, which often works pretty well:

>>> from curtsies.fmtfuncs import *
>>> f = blue(underline('As you like it'))
>>> f.center(20)
blue(underline("   As you like it   "))
>>> f.count('i')
2
>>> f.endswith('it')
True
>>> f.index('you')
3
>>> f.split(' ')
[blue(underline("As")), blue(underline("you")), blue(underline("like")), blue(underline("it"))]

But formatting information will be lost for attributes which are not the same throughout the initial string:

>>> from curtsies.fmtfuncs import *
>>> f = bold(red('hi')+' '+on_blue('there'))
>>> f
bold(red('hi'))+bold(' ')+bold(on_blue('there'))
>>> f.center(10)
bold(" hi there ")

FmtStr - Unicode

In Python 2, you might run into something like this:

>>> from curtsies.fmtfuncs import *
>>> red(u'hi')
red('hi')
>>> red('hi')
ValueError: unicode string required, got 'hi'

FmtStr requires unicode strings, so in Python 2 it is convenient to use the unicode_literals compiler directive:

>>> from __future__ import unicode_literals
>>> from curtsies.fmtfuncs import *
>>> red('hi')
red('hi')

FmtStr - len vs width

The amount of horizontal space a string takes up in a terminal may differ from the length of the string returned by len(). FmtStr objects have a width property useful when writing layout code:

>>> #encoding: utf8
...
>>> from curtsies.fmtfuncs import *
>>> fullwidth = blue(u'fullwidth')
>>> len(fullwidth), fullwidth.width, fullwidth.s
(9, 13, u'\uff46\uff55\uff4c\uff4cwidth')
>>> combined = red(u'a̤')
>>> len(combined), combined.width, combined.s
(2, 1, u'a\u0324')

As shown above, full width characters can take up two columns, and combining characters may be combined with the previous character to form a single grapheme. Curtsies uses a Python implementation of wcwidth to do this calculation.

FmtStr - API Docs

curtsies.fmtstr(string, *args, **kwargs)

Convenience function for creating a FmtStr

>>> fmtstr('asdf', 'blue', 'on_red', 'bold')
on_red(bold(blue('asdf')))
>>> fmtstr('blarg', fg='blue', bg='red', bold=True)
on_red(bold(blue('blarg')))
class curtsies.FmtStr(*components)

A string whose substrings carry attributes (which may be different from one to the next).

copy_with_new_atts(**attributes)

Returns a new FmtStr with the same content but new formatting

copy_with_new_str(new_str)

Copies the current FmtStr’s attributes while changing its string.

join(iterable)

Joins an iterable yielding strings or FmtStrs with self as separator

splice(new_str, start, end=None)

Returns a new FmtStr with the input string spliced into the the original FmtStr at start and end. If end is provided, new_str will replace the substring self.s[start:end-1].

split(sep=None, maxsplit=None, regex=False)

Split based on seperator, optionally using a regex

Capture groups are ignored in regex, the whole pattern is matched and used to split the original FmtStr.

width

The number of columns it would take to display this string

width_aware_slice(index)

Slice based on the number of columns it would take to display the substring

FmtStr instances respond to most str methods as you might expect, but the result of these methods sometimes loses its formatting.