Skip to main content

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)

* image

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')))

* image

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)

* image

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)

* image

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))

* image

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)

image

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: pytermor

> 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[0mhard 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-type 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(':'))

image

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)
  1. \x1b|1b is ESC control character, which opens a control sequence.

  2. [ is sequence introducer, it determines the type of control sequence (in this case it's CSI, or "Control Sequence Introducer").

  3. 4 and 7 are parameters of the escape sequence; they mean "underlined" and "inversed" attributes respectively. Those parameters must be separated by ;.

  4. 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())

image

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)

image

API: strf.StringFilter

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)

image

Usage with helper

Helper function apply_filters accepts both StringFilter 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)

image

API: strf.fmtd

Set of methods to make working with SGR sequences a bit easier.

  • ljust_fmtd() SGR-formatting-aware implementation of str.ljust()
  • rjust_fmtd() same, but for str.rjust()
  • center_fmtd() same, but for str.center()

API: numf.*

pytermor also includes a few helper formatters for numbers.

Details (click)

> format_auto_float

Dynamically adjust decimal digit amount to fill the output string up with significant digits as much as possible. Universal solution for situations when you don't know exaclty what values will be displayed, but have fixed output width. Invocation: format_auto_float(value, 4).

value result
1 234.56 "1235"
123.56 " 124"
12.56 "12.6"
1.56 "1.56"

> format_prefixed_unit

Similar to previous method, but this one also supports metric prefixes and is highly customizable. Invocation: format_prefixed_unit(value).

value 631 1 080 45 200 1 257 800 4,31×10⁷ 7,00×10⁸ 2,50×10⁹
result 631 b 1.05 kb 44.14 kb 1.20 Mb 41.11 Mb 668.0 Mb 2.33 Gb

Settings:

PrefixedUnitPreset(
    max_value_len=5, integer_input=True,
    unit='b', unit_separator=' ',
    mcoef=1024.0,
    prefixes=[None, 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'],
    prefix_zero_idx=0,
)

Example #2 illustrating small numbers:

value -1.2345×10⁻¹¹ 1.2345×10⁻⁸ 1.2345×10⁻⁴ 0.01234 1.23456 123.456 −12 345
result -0.012nm 0.0123μm 0.1235mm 0.0123m 1.2346m 123.46m -12.35km
PrefixedUnitPreset(
    max_value_len=6, integer_input=False,
    unit='m', unit_separator='',
    mcoef=1000.0,
    prefixes=['y', 'z', 'a', 'f', 'p', 'n', 'μ', 'm', None],
    prefix_zero_idx=8,
)

> format_time_delta

Formats time interval in 4 different variants - 3-char, 4-char, 6-char and 10-char width output. Usage: format_time_delta(seconds, max_len).

width 2 10 60 2700 32 340 273 600 4 752 000 8,64×10⁸
3 chars 2s 10s 1m 45m 8h 3d 55d --
4 chars 2 s 10 s 1 m 45 m 8 h 3 d 1 M 27 y
6 chars 2 sec 10 sec 1 min 45 min 8h 59m 3d 4h 1 mon 27 yr
10 chars 2 secs 10 secs 1 min 45 mins 8h 59m 3d 4h 1 months 27 years

Settings example (for 10-char mode):

TimeDeltaPreset([
    TimeUnit('sec', 60), 
    TimeUnit('min', 60, custom_short='min'), 
    TimeUnit('hour', 24, collapsible_after=24),
    TimeUnit('day', 30, collapsible_after=10),
    TimeUnit('month', 12),
    TimeUnit('year', overflow_afer=999),
], allow_negative=True,
    unit_separator=' ',
    plural_suffix='s',
    overflow_msg='OVERFLOW',
),

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_black BG_BLACK BG_COLOR_OFF
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.8.0

  • format_prefixed_unit extended for working with decimal and binary metric prefixes;
  • format_time_delta extended with new settings;
  • Value rounding transferred from format_auto_float to format_prefixed_unit;
  • Utility classes reorganization;
  • Unit tests output formatting;
  • noop SGR sequence and noop format;
  • Max decimal points for auto_float extended from (2) to (max-2).

v1.7.4

  • Added 3 formatters: fmt_prefixed_unit, fmt_time_delta, fmt_auto_float.

v1.7.3

  • Added bg_black format.

v1.7.2

  • Added ljust_fmtd, rjust_fmtd, center_fmtd util functions to align strings with SGRs correctly.

v1.7.1

  • Print reset sequence as \e[m instead of \e[0m.

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 to sgr 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.* and seq.* classes.

v1.3.2

  • Added gray and bg_gray format presets.

v1.3.1

  • Interface revisioning.

v1.2.1

  • opening_seq and closing_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytermor-1.8.0.tar.gz (35.8 kB view hashes)

Uploaded Source

Built Distribution

pytermor-1.8.0-py3-none-any.whl (23.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page