ANSI formatted terminal output library
Project description
pytermor
(yet another) Python library designed for formatting terminal output using ANSI escape codes. Implements automatic "soft" format termination. Provides a registry of ready-to-use SGR sequences and formats (=combined sequences).
Motivation
Key feature of this library is providing necessary abstractions for building complex text sections with lots of formatting, while keeping the application code clear and readable.
Installation
pip install pytermor
Use cases
Format is a combination of two control sequences; it wraps specified string with pre-defined leading and trailing SGR definitions.
from pytermor import fmt
print(fmt.blue('Use'), fmt.cyan('cases'))
Examples (click)
*
Preset formats can safely overlap with each other (as long as they require different breaker sequences to reset).
from pytermor import fmt
print(fmt.blue(fmt.underlined('Nested') + fmt.bold(' formats')))
*
Compose text formats with automatic content-aware format termination.
from pytermor import autof
fmt1 = autof('hi_cyan', 'bold')
fmt2 = autof('bg_black', 'inversed', 'underlined', 'italic')
msg = fmt1(f'Content{fmt2("-aware format")} nesting')
print(msg)
*
Create your own SGR sequences with build()
method, which accepts color/attribute keys, integer codes and even existing SGRs, in any amount and in any order. Key resolving is case-insensitive.
from pytermor import seq, build
seq1 = build('red', 1) # keys or integer codes
seq2 = build(seq1, seq.ITALIC) # existing SGRs as part of a new one
seq3 = build('underlined', 'YELLOW') # case-insensitive
msg = f'{seq1}Flexible{seq.RESET} ' + \
f'{seq2}sequence{seq.RESET} ' + \
str(seq3) + 'builder' + str(seq.RESET)
print(msg)
*
Use build_c256()
to set foreground/background color to any of ↗ xterm-256 colors.
from pytermor import build_c256, seq, autof
txt = '256 colors support'
start_color = 41
msg = ''
for idx, c in enumerate(range(start_color, start_color+(36*6), 36)):
msg += f'{build_c256(c)}{txt[idx*3:(idx+1)*3]}{seq.COLOR_OFF}'
print(autof(seq.BOLD).wrap(msg))
*
It's also possible to use 16M-color mode (or True color) — with build_rgb()
wrapper method.
from pytermor import build_rgb, seq, fmt
txt = 'True color support'
msg = ''
for idx, c in enumerate(range(0, 256, 256//18)):
r = max(0, 255-c)
g = max(0, min(255, 127-(c*2)))
b = c
msg += f'{build_rgb(r, g, b)}{txt[idx:(idx+1)]}{seq.COLOR_OFF}'
print(fmt.bold(msg))
Format soft reset
There are two ways to manage color and attribute termination:
- hard reset (SGR 0 |
\e[0m
) - soft reset (SGR 22, 23, 24 etc.)
The main difference between them is that hard reset disables all formatting after itself, while soft reset disables only actually necessary attributes (i.e. used as opening sequence in Format instance's context) and keeps the other.
That's what Format class and autof
method are designed for: to simplify creation of soft-resetting text spans, so that developer doesn't have to restore all previously applied formats after every closing sequence.
Example: we are given a text span which is initially bold and underlined. We want to recolor a few words inside of this span. By default this will result in losing all the formatting to the right of updated text span (because RESET
|\e[0m
clears all text attributes).
However, there is an option to specify what attributes should be disabled or let the library do that for you:
from pytermor import seq, fmt, autof, Format
# automatically:
fmt_warn = autof(seq.HI_YELLOW + seq.UNDERLINED)
# or manually:
fmt_warn = Format(
seq.HI_YELLOW + seq.UNDERLINED, # sequences can be summed up, remember?
seq.COLOR_OFF + seq.UNDERLINED_OFF, # "counteractive" sequences
hard_reset_after=False
)
orig_text = fmt.bold(f'this is {seq.BG_GRAY}the original{seq.RESET} string')
updated_text = orig_text.replace('original', fmt_warn('updated'), 1)
print(orig_text, '\n', updated_text)
As you can see, the update went well — we kept all the previously applied formatting. Of course, this method cannot be 100% applicable — for example, imagine that original text was colored blue. After the update "string" word won't be blue anymore, as we used COLOR_OFF
escape sequence to neutralize our own yellow color. But it still can be helpful for a majority of cases (especially when text is generated and formatted by the same program and in one go).
API: module
autof
Signature: autof(*params str|int|SequenceSGR) -> Format
Create new Format with specified control sequence(s) as a opening/starter sequence and automatically compose closing sequence that will terminate attributes defined in opening sequence while keeping the others (soft reset).
Resulting sequence params' order is the same as argument's order.
Each sequence param can be specified as:
- string key (see API: Registries)
- integer param value
- existing SequenceSGR instance (params will be extracted)
build
Signature: build(*params str|int|SequenceSGR) -> SequenceSGR
Create new SequenceSGR with specified params. Resulting sequence params order is the same as argument order. Parameter specification is the same as for autof
.
SequenceSGR with zero params was specifically implemented to translate into empty string and not into \e[m
, which wolud make sense, but also would be very entangling, as it's equivavlent of \e[0m
— hard reset sequence.
build_c256
Signature:build_c256(color: int, bg: bool = False) -> SequenceSGR
Create new SequenceSGR that sets foreground color or background color, depending on bg
value, in 256-color mode. Valid values for color
are [0; 255], see more at ↗ xterm-256 colors page.
build_rgb
Signature:build_rgb(r: int, g: int, b: int, bg: bool = False) -> SequenceSGR
Create new SequenceSGR that sets foreground color or background color, depending on bg
value, in 16M-color mode. Valid values for r
, g
and b
are [0; 255]; this range is linearly translated into [0x00; 0xFF] for each channel; the result value is composed as #RRGGBB.
API: SGR sequences
Class representing SGR-mode ANSI escape sequence with varying amount of parameters.
Details (click)
Creating the sequence
You can use any of predefined sequences from pytermor.seq
or create your own via standard constructor (see below). Valid argument values as well as preset constants are described in API: Registries section.
Applying the sequence
To get the resulting sequence chars use print()
method or cast instance to str:
from pytermor import SequenceSGR
seq = SequenceSGR(4, 7)
msg = f'({seq})'
print(msg + f'{SequenceSGR(0).print()}', str(msg.encode()), msg.encode().hex(':'))
1st part is "applied" escape sequence; 2nd part shows up a sequence in raw mode, as if it was ignored by the terminal; 3rd part is hexademical sequence byte values.
SGR sequence structure (click)
-
\x1b
|1b
is ESC control character, which opens a control sequence. -
[
is sequence introducer, it determines the type of control sequence (in this case it's CSI, or "Control Sequence Introducer"). -
4
and7
are parameters of the escape sequence; they mean "underlined" and "inversed" attributes respectively. Those parameters must be separated by;
. -
m
is sequence terminator; it also determines the sub-type of sequence, in our case SGR, or "Select Graphic Rendition". Sequences of this kind are most commonly encountered.
Combining SGRs
One instance of SequenceSGR can be added to another. This will result in a new SequenceSGR with combined params.
from pytermor import seq, SequenceSGR
combined = SequenceSGR(1, 31) + SequenceSGR(4)
print(f'{combined}combined{seq.RESET}', str(combined).encode())
API: Formats
Format is a wrapper class that contains starting (i.e. opening) SequenceSGR and (optionally) closing SequenceSGR.
Details (click)
Creating the format
You can define your own reusable Formats (see below) or import predefined ones from pytermor.fmt
(see API: Registries section).
Applying the format
Use wrap()
method of Format instance or call the instance itself to enclose specified string in starting/terminating SGR sequences:
from pytermor import seq, fmt, Format
fmt_error = Format(seq.BG_HI_RED + seq.UNDERLINED, seq.BG_COLOR_OFF + seq.UNDERLINED_OFF)
msg = fmt.italic.wrap('italic might ' + fmt_error('not') + ' work')
print(msg)
API: String filters
StringFilter is common string modifier interface with dynamic configuration support.
Details (click)
Implementations
- ReplaceSGR
- ReplaceCSI
- ReplaceNonAsciiBytes
Standalone usage
Can be applied using .apply()
method or with direct call.
from pytermor import fmt, ReplaceSGR
formatted = fmt.red('this text is red')
replaced = ReplaceSGR('[LIE]').apply(formatted)
# replaced = ReplaceSequenceSGRs('[LIE]')(formatted)
print(formatted, '\n', replaced)
Usage with helper
Helper function apply_filters
accepts both StringFilter
implementation instances and types, but latter is not configurable and will be invoked using default settings.
from pytermor import apply_filters, ReplaceNonAsciiBytes
ascii_and_binary = b'\xc0\xff\xeeQWE\xffRT\xeb\x00\xc0\xcd\xed'
result = apply_filters(ascii_and_binary, ReplaceNonAsciiBytes)
print(ascii_and_binary, '\n', result)
API: Registries
Sequences (click)
- code — SGR integer code(s) for specified sequence (order is important)
- name — variable name; usage:
from pytermor.seq import RESET
- key — string that will be recognised by
build()
|autof()
etc. - description — effect of applying the sequence / additional notes
As a rule of a thumb, key equals to name in lower case.
code | name | key | description |
---|---|---|---|
0 | RESET |
reset |
Reset all attributes and colors |
attributes | |||
1 | BOLD |
bold |
Bold or increased intensity |
2 | DIM |
dim |
Faint, decreased intensity |
3 | ITALIC |
italic |
Italic; not widely supported |
4 | UNDERLINED |
underlined |
Underline |
5 | BLINK_SLOW |
blink_slow |
Sets blinking to < 150 cpm |
6 | BLINK_FAST |
blink_fast |
150+ cpm; not widely supported |
7 | INVERSED |
inversed |
Swap foreground and background colors |
8 | HIDDEN |
hidden |
Conceal characters; not widely supported |
9 | CROSSLINED |
crosslined |
Strikethrough |
21 | DOUBLE_UNDERLINED |
double_underlined |
Double-underline; on several terminals disables BOLD instead |
53 | OVERLINED |
overlined |
Not widely supported |
breakers | |||
22 | BOLD_DIM_OFF |
bold_dim_off |
Disable BOLD and DIM attributes. Special aspects... It's impossible to reliably disable them on a separate basis. |
23 | ITALIC_OFF |
italic_off |
Disable italic |
24 | UNDERLINED_OFF |
underlined_off |
Disable underlining |
25 | BLINK_OFF |
blink_off |
Disable blinking |
27 | INVERSED_OFF |
inversed_off |
Disable inversing |
28 | HIDDEN_OFF |
hidden_off |
Disable conecaling |
29 | CROSSLINED_OFF |
crosslined_off |
Disable strikethrough |
39 | COLOR_OFF |
color_off |
Reset foreground color |
49 | BG_COLOR_OFF |
bg_color_off |
Reset bg color |
55 | OVERLINED_OFF |
overlined_off |
Disable overlining |
[foreground] colors | |||
30 | BLACK |
black |
Set foreground color to black |
31 | RED |
red |
Set foreground color to red |
32 | GREEN |
green |
Set foreground color to green |
33 | YELLOW |
yellow |
Set foreground color to yellow |
34 | BLUE |
blue |
Set foreground color to blue |
35 | MAGENTA |
magenta |
Set foreground color to magneta |
36 | CYAN |
cyan |
Set foreground color to cyan |
37 | WHITE |
white |
Set foreground color to white |
38;5 |
Use color_c256() instead
|
Set foreground color [256 mode] | |
38;2 |
Use color_rgb() instead
|
Set foreground color [16M mode] | |
background colors | |||
40 | BG_BLACK |
bg_black |
Set background color to black |
41 | BG_RED |
bg_red |
Set background color to red |
42 | BG_GREEN |
bg_green |
Set background color to green |
43 | BG_YELLOW |
bg_yellow |
Set background color to yellow |
44 | BG_BLUE |
bg_blue |
Set background color to blue |
45 | BG_MAGENTA |
bg_magenta |
Set background color to magenta |
46 | BG_CYAN |
bg_cyan |
Set background color to cyan |
47 | BG_WHITE |
bg_white |
Set background color to white |
48;5 |
Use color_c256() instead
|
Set background color [256 mode] | |
48;2 |
Use color_rgb() instead
|
Set background color [16M mode] | |
high-intensity [foreground] colors | |||
90 | GRAY |
gray |
Set foreground color to bright black/gray |
91 | HI_RED |
hi_red |
Set foreground color to bright red |
92 | HI_GREEN |
hi_green |
Set foreground color to bright green |
93 | HI_YELLOW |
hi_yellow |
Set foreground color to bright yellow |
94 | HI_BLUE |
hi_blue |
Set foreground color to bright blue |
95 | HI_MAGENTA |
hi_magenta |
Set foreground color to bright magenta |
96 | HI_CYAN |
hi_cyan |
Set foreground color to bright cyan |
97 | HI_WHITE |
hi_white |
Set foreground color to bright white |
high-intensity background colors | |||
100 | BG_GRAY |
bg_gray |
Set background color to bright black/gray |
101 | BG_HI_RED |
bg_hi_red |
Set background color to bright red |
102 | BG_HI_GREEN |
bg_hi_green |
Set background color to bright green |
103 | BG_HI_YELLOW |
bg_hi_yellow |
Set background color to bright yellow |
104 | BG_HI_BLUE |
bg_hi_blue |
Set background color to bright blue |
105 | BG_HI_MAGENTA |
bg_hi_magenta |
Set background color to bright magenta |
106 | BG_HI_CYAN |
bg_hi_cyan |
Set background color to bright cyan |
107 | BG_HI_WHITE |
bg_hi_white |
Set background color to bright white |
Formats (click)
- name — variable name; usage:
from pytermor.fmt import bold
- opening seq, closing seq — corresponding SGRs
As a rule of a thumb, name equals to opening seq in lower case.
name | opening seq | closing seq |
---|---|---|
attributes | ||
bold |
BOLD |
BOLD_DIM_OFF |
dim |
DIM |
BOLD_DIM_OFF |
italic |
ITALIC |
ITALIC_OFF |
underlined |
UNDERLINED |
UNDERLINED_OFF |
inversed |
INVERSED |
INVERSED_OFF |
overlined |
OVERLINED |
OVERLINED_OFF |
[foreground] colors | ||
red |
RED |
COLOR_OFF |
green |
GREEN |
COLOR_OFF |
yellow |
YELLOW |
COLOR_OFF |
blue |
BLUE |
COLOR_OFF |
magenta |
MAGENTA |
COLOR_OFF |
cyan |
CYAN |
COLOR_OFF |
gray |
GRAY |
COLOR_OFF |
background colors | ||
bg_red |
BG_RED |
BG_COLOR_OFF |
bg_green |
BG_GREEN |
BG_COLOR_OFF |
bg_yellow |
BG_YELLOW |
BG_COLOR_OFF |
bg_blue |
BG_BLUE |
BG_COLOR_OFF |
bg_magenta |
BG_MAGENTA |
BG_COLOR_OFF |
bg_cyan |
BG_CYAN |
BG_COLOR_OFF |
bg_gray |
BG_GRAY |
BG_COLOR_OFF |
You can of course create your own sequences and formats, but with one limitation — autoformatting will not work with custom defined sequences; unless you add the corresponding rule to pytermor.registry.sgr_parity_registry
.
Changelog
v1.7.0
Format()
constructor can be called without arguments.- Added SGR code lists.
v1.6.2
- Excluded
tests
dir from distribution package.
v1.6.1
- Ridded of EmptyFormat and AbstractFormat classes.
- Renamed
code
module tosgr
because of conflicts in PyCharm debugger (pydevd_console_integration.py
).
v1.5.0
- Removed excessive EmptySequenceSGR — default SGR class without params was specifically implemented to print out as empty string instead of
\e[m
.
v1.4.0
Format.wrap()
now accepts any type of argument, not only str.- Rebuilt Sequence inheritance tree.
- Added equality methods for Sequence and Format classes/subclasses.
- Added some tests for
fmt.*
andseq.*
classes.
v1.3.2
- Added
gray
andbg_gray
format presets.
v1.3.1
- Interface revisioning.
v1.2.1
opening_seq
andclosing_seq
properties for Format class.
v1.2.0
- EmptySequenceSGR and EmptyFormat classes.
v1.1.0
- Autoformat feature.
v1.0.0
- First public version.
References
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.