Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/delameter/pytermor
ANSI formatted terminal output library
https://github.com/delameter/pytermor
ansi color colored-terminal command-line console library linux python python3 sgr terminal tmux
Last synced: about 2 months ago
JSON representation
ANSI formatted terminal output library
- Host: GitHub
- URL: https://github.com/delameter/pytermor
- Owner: delameter
- License: gpl-3.0
- Created: 2022-03-06T06:55:39.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2024-02-28T15:43:35.000Z (11 months ago)
- Last Synced: 2024-11-08T08:08:26.690Z (2 months ago)
- Topics: ansi, color, colored-terminal, command-line, console, library, linux, python, python3, sgr, terminal, tmux
- Language: Python
- Homepage: https://pypi.org/p/pytermor
- Size: 93.8 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
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.
```python3
from pytermor import fmtprint(fmt.blue('Use'), fmt.cyan('cases'))
```Examples (click)
## * ![image](https://user-images.githubusercontent.com/50381946/161387692-4374edcb-c1fe-438f-96f1-dae3c5ad4088.png)
Preset formats can safely overlap with each other (as long as they require different **breaker** sequences to reset).
```python3
from pytermor import fmtprint(fmt.blue(fmt.underlined('Nested') + fmt.bold(' formats')))
```## * ![image](https://user-images.githubusercontent.com/50381946/161387711-23746520-419b-4917-9401-257854ff2d8a.png)
Compose text formats with automatic content-aware format termination.
```python3
from pytermor import autoffmt1 = autof('hi_cyan', 'bold')
fmt2 = autof('bg_black', 'inversed', 'underlined', 'italic')msg = fmt1(f'Content{fmt2("-aware format")} nesting')
print(msg)
```## * ![image](https://user-images.githubusercontent.com/50381946/161387734-677d5b10-15c1-4926-933f-b1144b0ce5cb.png)
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.
```python3
from pytermor import seq, buildseq1 = 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-insensitivemsg = f'{seq1}Flexible{seq.RESET} ' + \
f'{seq2}sequence{seq.RESET} ' + \
str(seq3) + 'builder' + str(seq.RESET)
print(msg)
```## * ![image](https://user-images.githubusercontent.com/50381946/161387746-0a94e3d2-8295-478c-828c-333e99e5d50a.png)
Use `build_c256()` to set foreground/background color to any of [↗ xterm-256 colors](https://www.ditig.com/256-colors-cheat-sheet).
```python3
from pytermor import build_c256, seq, autoftxt = '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](https://user-images.githubusercontent.com/50381946/161411577-743b9a81-eac3-47c0-9b59-82b289cc0f45.png)
It's also possible to use 16M-color mode (or True color) — with `build_rgb()` wrapper method.
```python3
from pytermor import build_rgb, seq, fmttxt = '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:
```python3
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](https://user-images.githubusercontent.com/50381946/163714299-1f7d3d52-0b9a-4d3e-91bf-26e8cce9b1f1.png)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](#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](https://www.ditig.com/256-colors-cheat-sheet) 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](#api-registries) section.
### Applying the sequence
To get the resulting sequence chars use `print()` method or cast instance to _str_:
```python3
from pytermor import SequenceSGRseq = SequenceSGR(4, 7)
msg = f'({seq})'
print(msg + f'{SequenceSGR(0).print()}', str(msg.encode()), msg.encode().hex(':'))
```
> ![image](https://user-images.githubusercontent.com/50381946/161387861-5203fff8-86c8-4c52-8518-63a5525c09f7.png)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.
```python3
from pytermor import seq, SequenceSGRcombined = SequenceSGR(1, 31) + SequenceSGR(4)
print(f'{combined}combined{seq.RESET}', str(combined).encode())
```
> ![image](https://user-images.githubusercontent.com/50381946/161387867-808831e5-784b-49ec-9c24-490734ef4eab.png)## 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](#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:
```python3
from pytermor import seq, fmt, Formatfmt_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](https://user-images.githubusercontent.com/50381946/161387874-5c25a493-253b-4f9e-8dbf-8328add2e5d5.png)## 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.
```python3
from pytermor import fmt, ReplaceSGRformatted = fmt.red('this text is red')
replaced = ReplaceSGR('[LIE]').apply(formatted)
# replaced = ReplaceSequenceSGRs('[LIE]')(formatted)print(formatted, '\n', replaced)
```
> ![image](https://user-images.githubusercontent.com/50381946/161387885-0fc0fcb5-09aa-4258-aa25-312220e7f994.png)### 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.
```python3
from pytermor import apply_filters, ReplaceNonAsciiBytesascii_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](https://user-images.githubusercontent.com/50381946/161387889-a1920f13-f5fc-4d10-b535-93f1a1b1aa5c.png)## 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:
```python
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
```python
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):
```python
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 notesAs 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 disablesBOLD
instead
53
OVERLINED
overlined
Not widely supported
breakers
22
BOLD_DIM_OFF
bold_dim_off
DisableBOLD
andDIM
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
Usecolor_c256()
instead
Set foreground color [256 mode]
38;2
Usecolor_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
Usecolor_c256()
instead
Set background color [256 mode]
48;2
Usecolor_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 SGRsAs 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
- https://en.wikipedia.org/wiki/ANSI_escape_code
- [ANSI Escape Sequences](https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797)