Package: ansimarkup
AnsimarkupAnsimarkup is an XML-like markup for producing colored terminal text.fromansimarkupimportansiprintasprintprint("<b>bold text</b>"))print("<red>red text</red>","<red,green>red text on a green background</red,green>")print("<fg #ffaf00>orange text</fg #ffaf00>")InstallationThe latest stable version of ansimarkup can be installed from PyPi:python3-mpipinstallansimarkupUsageBasicfromansimarkupimportparse,ansiprint# parse() converts the tags to the corresponding ansi escape sequence.parse("<b>bold</b> <d>dim</d>")# ansiprint() works exactly like print(), but first runs parse() on all arguments.ansiprint("<b>bold</b>","<d>dim</d>")ansiprint("<b>bold</b>","<d>dim</d>",sep=":",file=sys.stderr)Colors and styles# Colors may be specified in one of several ways.parse("<red>red foreground</red>")parse("<RED>red background</RED>")parse("<fg red>red foreground</fg red>")parse("<bg red>red background</bg red>")# Xterm, hex and rgb colors are accepted by the <fg> and <bg> tags.parse("<fg 86>aquamarine foreground</fg 86>")parse("<bg #00005f>dark blue background</bg #00005f>")parse("<fg 0,95,0>dark green foreground</fg 0,95,0>")# Tags may be nested.parse("<r><Y>red text on a yellow foreground</Y></r>")# The above may be more concisely written as:parse("<r,y>red text on a yellow background</r,y>")# This shorthand also supports style tags.parse("<b,r,y>bold red text on a yellow background</b,r,y>")parse("<b,r,>bold red text</b,r,>")parse("<b,,y>bold regular text on a yellow background</b,,y>")# Unrecognized tags are left as-is.parse("<b><element1></element1></b>")For a list of markup tags, please refer totags.py.User-defined tagsCustom tags or overrides for existing tags may be defined by creating a
newAnsiMarkupinstance:fromansimarkupimportAnsiMarkup,parseuser_tags={# Add a new tag (e.g. we want <info> to expand to "<bold><green>")."info":parse("<b><g>")# The ansi escape sequence can be used directly."info":"e\x1b[32m\x1b[1m",# Tag names may also be callables."err":lambda:parse("<r>")# Colors may also be given convenient tag names."orange":parse("<fg #d78700>"),# User-defined tags always take precedence over existing tags."bold":parse("<dim>")}am=AnsiMarkup(tags=user_tags)am.parse("<info>bold green</info>")am.ansiprint("<err>red</err>")# Calling the instance is equivalent to calling its parse method.am("<b>bold</b>")==am.parse("<b>bold</b>")Alignment and lengthAligning formatted strings can be challenging because the length of the
rendered string is different that the number of printable characters.
Consider this example:>>>a='|{:30}|'.format('abc')>>>b='|{:30}|'.format(parse('<b>abc</b>'))>>>print(a,b,sep='\n')| abc                    || abc                            |This can be addressed by using theansistringfunction or theAnsiMarkup.string(markup)method, which has the following useful
properties:>>>s=ansistring('<b>abc</b>')>>>print(repr(s),'->',s)<b>abc</b> -> abc  # abc is printed in bold>>>len(s),len(am.parse('<b>abc</b>'),s.delta3, 11, 8With the help of thedeltaproperty, it is easy to align the strings
in the above example:>>>s=ansistring('<b>abc</b>')>>>a='| {:{width}} |'.format('abc',width=30)>>>b='| {:{width}} |'.format(s,width=(30+s.delta))>>>print(a,b,sep='\n')| abc                            || abc                            |Escaping raw stringsBothansiprint()andparse()pass arguments of typerawuntouched.>>>fromansimarkupimportansiprint,parse,raw>>>ansiprint("<b><r>",raw("<l type='V'>2.0</l>"),"</r></b>")<l type='V'>2.0</l>  # printed in bold red (note the leading space caused)>>>s=parse("<b><r>",raw("<l type='V'>2.0</l>"),"</r></b>")>>>print(s)<l type='V'>2.0</l>  # printed in bold redBuilding a template string may also be sufficient:>>>fromansimarkupimportparse>>>s=parse("<b><r>%s</r></b>")>>>print(s%"<l type='V'>2.0</l>")<l type='V'>2.0</l>  # printed in bold redOther featuresThe default tag separators can be changed by passing thetag_separgument toAnsiMarkup:fromansimarkupimportAnsiMarkupam=AnsiMarkup(tag_sep="{}")am.parse("{b}{r}bold red{/b}{/r}")Markup tags can be removed using thestrip()method:fromansimarkupimportAnsiMarkupam=AnsiMarkup()am.strip("<b><r>bold red</b></r>")Thestrictoption instructs the parser to raiseMismatchedTagif
opening tags don't have corresponding closing tags:fromansimarkupimportAnsiMarkupam=AnsiMarkup(strict=True)am.parse("<r><b>bold red")# ansimarkup.MismatchedTag: opening tag "<r>" has no corresponding closing tagCommand-lineAnsimarkup may also be used on the command-line. This works as if all
arguments were passed toansiprint():$ python -m ansimarkup
Usage: python -m ansimarkup [<arg> [<arg> ...]]

Example usage:
  python -m ansimarkup '<b>Bold</b>' '<r>Red</r>'
  python -m ansimarkup '<b><r>Bold Red</r></b>'
  python -m ansimarkup < input-with-markup.txt
  echo '<b>Bold</b>' | python -m ansimarkupLogging formatterAnsimarkup also comes with a formatter for the standard libraryloggingmodule. It can be used as:importloggingfromansimarkup.logformatterimportAnsiMarkupFormatterlog=logging.getLogger()hdl=logging.StreamHandler()fmt=AnsiMarkupFormatter()hdl.setFormatter(fmt)log.addHandler(hdl)log.info("<b>bold text</b>")WindowsAnsimarkup uses thecoloramalibrary internally, which means that
Windows support for ansi escape sequences is available by first running:importcoloramacolorama.init()For more information on Windows support, consult the "Usage" section
of thecoloramadocumentation.PerformanceWhile the focus of ansimarkup is convenience, it does try to keep
processing to a minimum. Thebenchmark.pyscript attempts to benchmark
different ansi escape code libraries:Benchmark 1: <r><b>red bold</b></r>
  colorama     0.1959 μs
  colr         1.8022 μs
  ansimarkup   3.1681 μs
  termcolor    5.3734 μs
  rich         9.0673 μs
  pastel       10.7440 μs
  plumbum      14.0620 μs

Benchmark 2: <r><b>red bold</b>red</r><b>bold</b>
  colorama     0.5360 μs
  colr         4.5575 μs
  ansimarkup   4.5727 μs
  termcolor    15.8462 μs
  rich         21.2631 μs
  pastel       22.9391 μs
  plumbum      33.1179 μsLimitationsAnsimarkup is a simple wrapper aroundcolorama. It does very little in
the way of validating that markup strings are well-formed. This is a
conscious decision with the goal of keeping things simple and fast.Unbalanced nesting, such as in the following example, will produce
incorrect output:<r><Y>1</r>2</Y>TodoMany corner cases remain to be fixed.More elaborate testing. The current test suite mostly covers the "happy paths".Replacetag_list.indexinsub_endwith something more efficient (i.e. something like an ordered MultiDict).Similar librariespastel: bring colors to your terminalplumbum.colors: small yet feature-rich library for shell script-like programs in Pythoncolr: easy terminal colors, with chainable methodsrich: rich text and beautiful formatting in the terminal (seerich.print()andrich.markup.render())LicenseAnsimarkup is released under the terms of theRevised BSD License.