Package: pytermor
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).MotivationKey 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.Installationpip install pytermorUse casesFormatis a combination of two control sequences; it wraps specified string with pre-defined leading and trailing SGR definitions.frompytermorimportfmtprint(fmt.blue('Use'),fmt.cyan('cases'))Examples(click)*Preset formats can safely overlap with each other (as long as they require differentbreakersequences to reset).frompytermorimportfmtprint(fmt.blue(fmt.underlined('Nested')+fmt.bold(' formats')))*Compose text formats with automatic content-aware format termination.frompytermorimportautoffmt1=autof('hi_cyan','bold')fmt2=autof('bg_black','inversed','underlined','italic')msg=fmt1(f'Content{fmt2("-aware format")}nesting')print(msg)*Create your ownSGRsequenceswithbuild()method, which accepts color/attribute keys, integer codes and even existingSGRs, in any amount and in any order. Key resolving is case-insensitive.frompytermorimportseq,buildseq1=build('red',1)# keys or integer codesseq2=build(seq1,seq.ITALIC)# existing SGRs as part of a new oneseq3=build('underlined','YELLOW')# case-insensitivemsg=f'{seq1}Flexible{seq.RESET}'+\f'{seq2}sequence{seq.RESET}'+\str(seq3)+'builder'+str(seq.RESET)print(msg)*Usebuild_c256()to set foreground/background color to any of↗ xterm-256 colors.frompytermorimportbuild_c256,seq,autoftxt='256 colors support'start_color=41msg=''foridx,cinenumerate(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) — withbuild_rgb()wrapper method.frompytermorimportbuild_rgb,seq,fmttxt='True color support'msg=''foridx,cinenumerate(range(0,256,256//18)):r=max(0,255-c)g=max(0,min(255,127-(c*2)))b=cmsg+=f'{build_rgb(r,g,b)}{txt[idx:(idx+1)]}{seq.COLOR_OFF}'print(fmt.bold(msg))Format soft resetThere 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 thathardreset disables all formatting after itself, whilesoftreset disables only actually necessary attributes (i.e. used as opening sequence inFormatinstance's context) and keeps the other.That's whatFormatclass andautofmethod 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 initiallyboldand 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 (becauseRESET|\e[0mclears all text attributes).However, there is an option to specify what attributes should be disabled or let the library do that for you:frompytermorimportseq,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" sequenceshard_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 usedCOLOR_OFFescape 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>autofSignature:autof(*params str|int|SequenceSGR) -> FormatCreate newFormatwith specified control sequence(s) as a opening/starter sequence andautomatically composeclosing 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 (seeAPI: Registries)integer param valueexistingSequenceSGRinstance (params will be extracted)>buildSignature:build(*params str|int|SequenceSGR) -> SequenceSGRCreate newSequenceSGRwith specified params. Resulting sequence params order is the same as argument order. Parameter specification is the same as forautof.SequenceSGRwith 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 resetsequence.>build_c256Signature:build_c256(color: int, bg: bool = False) -> SequenceSGRCreate newSequenceSGRthat sets foreground color or background color, depending onbgvalue, in 256-color mode. Valid values forcolorare [0; 255], see more at↗ xterm-256 colorspage.>build_rgbSignature:build_rgb(r: int, g: int, b: int, bg: bool = False) -> SequenceSGRCreate newSequenceSGRthat sets foreground color or background color, depending onbgvalue, in 16M-color mode. Valid values forr,gandbare [0; 255]; this range is linearly translated into [0x00; 0xFF] for each channel; the result value is composed as #RRGGBB.API: SGR sequencesClass representing SGR-type ANSI escape sequence with varying amount of parameters.Details(click)Creating the sequenceYou can use any of predefined sequences frompytermor.seqor create your own via standard constructor (see below). Valid argument values as well as preset constants are described inAPI: Registriessection.Applying the sequenceTo get the resulting sequence chars useprint()method or cast instance tostr:frompytermorimportSequenceSGRseq=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|1bis ESCcontrol character, which opens a control sequence.[is sequenceintroducer, it determines the type of control sequence (in this case it'sCSI, or "Control Sequence Introducer").4and7areparametersof the escape sequence; they mean "underlined" and "inversed" attributes respectively. Those parameters must be separated by;.mis sequenceterminator; it also determines the sub-type of sequence, in our caseSGR, or "Select Graphic Rendition". Sequences of this kind are most commonly encountered.Combining SGRsOne instance ofSequenceSGRcan be added to another. This will result in a newSequenceSGRwith combined params.frompytermorimportseq,SequenceSGRcombined=SequenceSGR(1,31)+SequenceSGR(4)print(f'{combined}combined{seq.RESET}',str(combined).encode())API: FormatsFormatis a wrapper class that contains starting (i.e. opening)SequenceSGRand (optionally) closingSequenceSGR.Details(click)Creating the formatYou can define your own reusableFormats (see below) or import predefined ones frompytermor.fmt(seeAPI: Registriessection).Applying the formatUsewrap()method ofFormatinstance or call the instance itself to enclose specified string in starting/terminating SGR sequences:frompytermorimportseq,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)API: strf.StringFilterStringFilteris common string modifier interface with dynamic configuration support.Details(click)ImplementationsReplaceSGRReplaceCSIReplaceNonAsciiBytesStandalone usageCan be applied using.apply()method or with direct call.frompytermorimportfmt,ReplaceSGRformatted=fmt.red('this text is red')replaced=ReplaceSGR('[LIE]').apply(formatted)# replaced = ReplaceSequenceSGRs('[LIE]')(formatted)print(formatted,'\n',replaced)Usage with helperHelper functionapply_filtersaccepts bothStringFilterinstances and types, but latter is not configurable and will be invoked using default settings.frompytermorimportapply_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)API: strf.fmtdSet of methods to make working with SGR sequences a bit easier.ljust_fmtd()SGR-formatting-aware implementation of str.ljust()rjust_fmtd()same, but forstr.rjust()center_fmtd()same, but forstr.center()API: numf.*pytermoralso includes a few helper formatters for numbers.Details(click)>format_auto_floatDynamically 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).valueresult1 234.56"1235"123.56" 124"12.56"12.6"1.56"1.56">format_prefixed_unitSimilar to previous method, but this one also supports metric prefixes and is highly customizable. Invocation:format_prefixed_unit(value).value6311 08045 2001 257 8004,31×10⁷7,00×10⁸2,50×10⁹result631 b1.05 kb44.14 kb1.20 Mb41.11 Mb668.0 Mb2.33 GbSettings: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.012341.23456123.456−12 345result-0.012nm0.0123μm0.1235mm0.0123m1.2346m123.46m-12.35kmPrefixedUnitPreset(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_deltaFormats time interval in 4 different variants - 3-char, 4-char, 6-char and 10-char width output. Usage:format_time_delta(seconds, max_len).width21060270032 340273 6004 752 0008,64×10⁸3 chars2s10s1m45m8h3d55d--4 chars2 s10 s1 m45 m8 h3 d1 M27 y6 chars2 sec10 sec1 min45 min8h 59m3d 4h1 mon27 yr10 chars2 secs10 secs1 min45 mins8h 59m3d 4h1 months27 yearsSettings 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: RegistriesSequences(click)code— SGR integer code(s) for specified sequence (order is important)name— variable name; usage:from pytermor.seq import RESETkey— string that will be recognised bybuild()|autof()etc.description— effect of applying the sequence / additional notesAs a rule of a thumb,keyequals tonamein lower case.codenamekeydescription0RESETresetReset all attributes and colorsattributes1BOLDboldBold or increased intensity2DIMdimFaint, decreased intensity3ITALICitalicItalic; not widely supported4UNDERLINEDunderlinedUnderline5BLINK_SLOWblink_slowSets blinking to < 150 cpm6BLINK_FASTblink_fast150+ cpm; not widely supported7INVERSEDinversedSwap foreground and background colors8HIDDENhiddenConceal characters; not widely supported9CROSSLINEDcrosslinedStrikethrough21DOUBLE_UNDERLINEDdouble_underlinedDouble-underline; on several terminals disablesBOLDinstead53OVERLINEDoverlinedNot widely supportedbreakers22BOLD_DIM_OFFbold_dim_offDisableBOLDandDIMattributes.Special aspects... It's impossible to reliably disable them on a separate basis.23ITALIC_OFFitalic_offDisable italic24UNDERLINED_OFFunderlined_offDisable underlining25BLINK_OFFblink_offDisable blinking27INVERSED_OFFinversed_offDisable inversing28HIDDEN_OFFhidden_offDisable conecaling29CROSSLINED_OFFcrosslined_offDisable strikethrough39COLOR_OFFcolor_offReset foreground color49BG_COLOR_OFFbg_color_offReset bg color55OVERLINED_OFFoverlined_offDisable overlining[foreground] colors30BLACKblackSet foreground color to black31REDredSet foreground color to red32GREENgreenSet foreground color to green33YELLOWyellowSet foreground color to yellow34BLUEblueSet foreground color to blue35MAGENTAmagentaSet foreground color to magneta36CYANcyanSet foreground color to cyan37WHITEwhiteSet foreground color to white38;5Usecolor_c256()insteadSet foreground color [256 mode]38;2Usecolor_rgb()insteadSet foreground color [16M mode]background colors40BG_BLACKbg_blackSet background color to black41BG_REDbg_redSet background color to red42BG_GREENbg_greenSet background color to green43BG_YELLOWbg_yellowSet background color to yellow44BG_BLUEbg_blueSet background color to blue45BG_MAGENTAbg_magentaSet background color to magenta46BG_CYANbg_cyanSet background color to cyan47BG_WHITEbg_whiteSet background color to white48;5Usecolor_c256()insteadSet background color [256 mode]48;2Usecolor_rgb()insteadSet background color [16M mode]high-intensity [foreground] colors90GRAYgraySet foreground color to bright black/gray91HI_REDhi_redSet foreground color to bright red92HI_GREENhi_greenSet foreground color to bright green93HI_YELLOWhi_yellowSet foreground color to bright yellow94HI_BLUEhi_blueSet foreground color to bright blue95HI_MAGENTAhi_magentaSet foreground color to bright magenta96HI_CYANhi_cyanSet foreground color to bright cyan97HI_WHITEhi_whiteSet foreground color to bright whitehigh-intensity background colors100BG_GRAYbg_graySet background color to bright black/gray101BG_HI_REDbg_hi_redSet background color to bright red102BG_HI_GREENbg_hi_greenSet background color to bright green103BG_HI_YELLOWbg_hi_yellowSet background color to bright yellow104BG_HI_BLUEbg_hi_blueSet background color to bright blue105BG_HI_MAGENTAbg_hi_magentaSet background color to bright magenta106BG_HI_CYANbg_hi_cyanSet background color to bright cyan107BG_HI_WHITEbg_hi_whiteSet background color to bright whiteFormats(click)name— variable name; usage:from pytermor.fmt import boldopening seq,closing seq— correspondingSGRsAs a rule of a thumb,nameequals toopening seqin lower case.nameopening seqclosing seqattributesboldBOLDBOLD_DIM_OFFdimDIMBOLD_DIM_OFFitalicITALICITALIC_OFFunderlinedUNDERLINEDUNDERLINED_OFFinversedINVERSEDINVERSED_OFFoverlinedOVERLINEDOVERLINED_OFF[foreground] colorsredREDCOLOR_OFFgreenGREENCOLOR_OFFyellowYELLOWCOLOR_OFFblueBLUECOLOR_OFFmagentaMAGENTACOLOR_OFFcyanCYANCOLOR_OFFgrayGRAYCOLOR_OFFbackground colorsbg_blackBG_BLACKBG_COLOR_OFFbg_redBG_REDBG_COLOR_OFFbg_greenBG_GREENBG_COLOR_OFFbg_yellowBG_YELLOWBG_COLOR_OFFbg_blueBG_BLUEBG_COLOR_OFFbg_magentaBG_MAGENTABG_COLOR_OFFbg_cyanBG_CYANBG_COLOR_OFFbg_grayBG_GRAYBG_COLOR_OFFYou 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 topytermor.registry.sgr_parity_registry.Changelogv1.8.0format_prefixed_unitextended for working with decimal and binary metric prefixes;format_time_deltaextended with new settings;Value rounding transferred fromformat_auto_floattoformat_prefixed_unit;Utility classes reorganization;Unit tests output formatting;noopSGR sequence andnoopformat;Max decimal points forauto_floatextended from (2) to (max-2).v1.7.4Added 3 formatters:fmt_prefixed_unit,fmt_time_delta,fmt_auto_float.v1.7.3Addedbg_blackformat.v1.7.2Addedljust_fmtd,rjust_fmtd,center_fmtdutil functions to align strings with SGRs correctly.v1.7.1Print reset sequence as\e[minstead of\e[0m.v1.7.0Format()constructor can be called without arguments.Added SGR code lists.v1.6.2Excludedtestsdir from distribution package.v1.6.1Ridded ofEmptyFormatandAbstractFormatclasses.Renamedcodemodule tosgrbecause of conflicts in PyCharm debugger (pydevd_console_integration.py).v1.5.0Removed excessiveEmptySequenceSGR— defaultSGRclass without params was specifically implemented to print out as empty string instead of\e[m.v1.4.0Format.wrap()now accepts any type of argument, not onlystr.RebuiltSequenceinheritance tree.Added equality methods forSequenceandFormatclasses/subclasses.Added some tests forfmt.*andseq.*classes.v1.3.2Addedgrayandbg_grayformat presets.v1.3.1Interface revisioning.v1.2.1opening_seqandclosing_seqproperties forFormatclass.v1.2.0EmptySequenceSGRandEmptyFormatclasses.v1.1.0Autoformat feature.v1.0.0First public version.Referenceshttps://en.wikipedia.org/wiki/ANSI_escape_codeANSI Escape Sequences