From 6be45f6cd2d2022a32d1ea40c09bbbad86061db1 Mon Sep 17 00:00:00 2001 From: Matthias Geier Date: Mon, 25 Jan 2016 08:50:32 +0100 Subject: [PATCH 1/6] Re-write ANSI color handling Closes #214. As a side effect, this also fixes #174. --- nbconvert/filters/ansi.py | 351 ++++++++++++++++++---------- nbconvert/templates/latex/base.tplx | 4 + nbconvert/utils/coloransi.py | 70 ------ 3 files changed, 234 insertions(+), 191 deletions(-) delete mode 100644 nbconvert/utils/coloransi.py diff --git a/nbconvert/filters/ansi.py b/nbconvert/filters/ansi.py index f2e66b4e9..247dd9a6c 100644 --- a/nbconvert/filters/ansi.py +++ b/nbconvert/filters/ansi.py @@ -1,155 +1,264 @@ -"""Filters for processing ANSI colors within Jinja templates. -""" +"""Filters for processing ANSI colors within Jinja templates.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. import re -from nbconvert.utils import coloransi +import jinja2 from ipython_genutils.text import strip_ansi - __all__ = [ 'strip_ansi', 'ansi2html', - 'single_ansi2latex', 'ansi2latex' ] -ansi_colormap = { - '30': 'ansiblack', - '31': 'ansired', - '32': 'ansigreen', - '33': 'ansiyellow', - '34': 'ansiblue', - '35': 'ansipurple', - '36': 'ansicyan', - '37': 'ansigrey', - '01': 'ansibold', -} - -html_escapes = { - '<': '<', - '>': '>', - "'": ''', - '"': '"', - '`': '`', -} -ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m') +_FG_HTML = ( + 'ansiblack', + 'ansired', + 'ansigreen', + 'ansiyellow', + 'ansiblue', + 'ansipurple', + 'ansicyan', + 'ansigray', +) + +_BG_HTML = ( + 'ansibgblack', + 'ansibgred', + 'ansibggreen', + 'ansibgyellow', + 'ansibgblue', + 'ansibgpurple', + 'ansibgcyan', + 'ansibggray', +) + +_FG_LATEX = ( + 'black', + 'red', + 'green', + 'brown', + 'blue', + 'purple', + 'cyan', + 'lightgray', +) + +_BG_LATEX = ( + 'darkgray', + 'lightred', + 'lightgreen', + 'yellow', + 'lightblue', + 'lightpurple', + 'lightcyan', + 'white', +) + def ansi2html(text): """ - Convert ansi colors to html colors. - + Convert ANSI colors to HTML colors. + Parameters ---------- text : str - Text containing ansi colors to convert to html + Text containing ANSI colors to convert to HTML + """ + text = str(jinja2.utils.escape(text)) + return _ansi2anything(text, _htmlconverter) - # do ampersand first - text = text.replace('&', '&') - - for c, escape in html_escapes.items(): - text = text.replace(c, escape) - - m = ansi_re.search(text) - opened = False - cmds = [] - opener = '' - closer = '' - while m: - cmds = m.groups()[0].split(';') - closer = '' if opened else '' - - # True if there is there more than one element in cmds, *or* - # if there is only one but it is not equal to a string of zeroes. - opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0]) - classes = [] - for cmd in cmds: - if cmd in ansi_colormap: - classes.append(ansi_colormap[cmd]) - - if classes: - opener = '' % (' '.join(classes)) - else: - opener = '' - text = re.sub(ansi_re, closer + opener, text, 1) - m = ansi_re.search(text) +def ansi2latex(text): + """ + Convert ANSI colors to LaTeX colors. + + Parameters + ---------- + text : str + Text containing ANSI colors to convert to LaTeX + + """ + return _ansi2anything(text, _latexconverter) + + +def _htmlconverter(fg, bg, bold): + """ + Return start and end tags for given foreground/background/bold. + + """ + if (fg, bg, bold) == (None, None, False): + return '', '' + + classes = [] + styles = [] - if opened: - text += '' - return text + if isinstance(fg, int): + classes.append(_FG_HTML[fg]) + elif fg: + styles.append('color: rgb({},{},{})'.format(*fg)) + if isinstance(bg, int): + classes.append(_BG_HTML[bg]) + elif bg: + styles.append('background-color: rgb({},{},{})'.format(*bg)) -def single_ansi2latex(code): - """Converts single ansi markup to latex format. + if bold: + classes.append('ansibold') - Return latex code and number of open brackets. + starttag = '' - Accepts codes like '\x1b[1;32m' (bold, red) and the short form '\x1b[32m' (red) - Colors are matched to those defined in coloransi, which defines colors - using the 0, 1 (bold) and 5 (blinking) styles. Styles 1 and 5 are - interpreted as bold. All other styles are mapped to 0. Note that in - coloransi, a style of 1 does not just mean bold; for example, Brown is - "0;33", but Yellow is "1;33". An empty string is returned for unrecognised - codes and the "reset" code '\x1b[m'. +def _latexconverter(fg, bg, bold): """ - components = code.split(';') - if len(components) > 1: - # Style is digits after '[' - style = int(components[0].split('[')[-1]) - color = components[1][:-1] - else: - style = 0 - color = components[0][-3:-1] - - # If the style is not normal (0), bold (1) or blinking (5) then treat it as normal - if style not in [0, 1, 5]: - style = 0 - - for name, tcode in coloransi.color_templates: - tstyle, tcolor = tcode.split(';') - tstyle = int(tstyle) - if tstyle == style and tcolor == color: - break - else: - return '', 0 + Return start and end markup given foreground/background/bold. + + """ + if (fg, bg, bold) == (None, None, False): + return '', '' - if style == 5: - name = name[5:] # BlinkRed -> Red, etc - name = name.lower() + starttag, endtag = '', '' - if style in [1, 5]: - return r'\textbf{\color{'+name+'}', 1 - else: - return r'{\color{'+name+'}', 1 + if isinstance(fg, int): + starttag += r'\textcolor{' + _FG_LATEX[fg] + '}{' + endtag = '}' + endtag + elif fg: + starttag += r'\textcolorRGB{%s}{%s}{%s}{' % fg + endtag = '}' + endtag -def ansi2latex(text): - """Converts ansi formated text to latex version + if isinstance(bg, int): + starttag += r'\setlength{\fboxsep}{0pt}\colorbox{' + starttag += _BG_LATEX[bg] + '}{' + endtag = r'\strut}' + endtag + elif bg: + starttag += r'\setlength{\fboxsep}{0pt}\colorboxRGB{%s}{%s}{%s}{' % bg + endtag = r'\strut}' + endtag + + if bold: + starttag += r'\textbf{' + endtag = '}' + endtag + return starttag, endtag + + +def _ansi2anything(text, converter): + r""" + Convert ANSI colors to HTML or LaTeX. + + See https://en.wikipedia.org/wiki/ANSI_escape_code + + Accepts codes like '\x1b[32m' (red) and '\x1b[1;32m' (bold, red). + The codes 1 (bold) and 5 (blinking) are selecting a bold font, code + 0 and an empty code ('\x1b[m') reset colors and bold-ness. + Unlike in most terminals, "bold" doesn't change the color. + The codes 21 and 22 deselect "bold", the codes 39 and 49 deselect + the foreground and background color, respectively. + The codes 38 and 48 select the "extended" set of foreground and + background colors, respectively. + + Non-color escape sequences (not ending with 'm') are filtered out. + + Ideally, this should have the same behavior as the function + fixConsole() in notebook/notebook/static/base/js/utils.js. - based on https://bitbucket.org/birkenfeld/sphinx-contrib/ansi.py """ - color_pattern = re.compile('\x1b\\[([^m]*)m') - last_end = 0 - openbrack = 0 - outstring = '' - for match in color_pattern.finditer(text): - head = text[last_end:match.start()] - outstring += head - if openbrack: - outstring += '}'*openbrack - openbrack = 0 - code = match.group() - if not (code == coloransi.TermColors.Normal or openbrack): - texform, openbrack = single_ansi2latex(code) - outstring += texform - last_end = match.end() - - # Add the remainer of the string and THEN close any remaining color brackets. - outstring += text[last_end:] - if openbrack: - outstring += '}'*openbrack - return outstring.strip() + ansi_re = re.compile('\x1b\\[([^@-~]*)([@-~])') + fg, bg = None, None + bold = False + numbers = [] + out = [] + + while text: + m = ansi_re.search(text) + if m: + if m.group(2) == 'm': + try: + numbers = [int(n) if n else 0 + for n in m.group(1).split(';')] + except ValueError: + pass # Invalid color specification + else: + pass # Not a color code + chunk, text = text[:m.start()], text[m.end():] + else: + chunk, text = text, '' + + if chunk: + starttag, endtag = converter(fg, bg, bold) + out.append(starttag) + out.append(chunk) + out.append(endtag) + + while numbers: + n = numbers.pop(0) + if n == 0: + fg = bg = None + bold = False + elif n in (1, 5): + bold = True + elif n in (21, 22): + bold = False + elif 30 <= n <= 37: + fg = n - 30 + elif n == 38: + try: + fg = _get_extended_color(numbers) + except ValueError: + numbers.clear() + elif n == 39: + fg = None + elif 40 <= n <= 47: + bg = n - 40 + elif n == 48: + try: + bg = _get_extended_color(numbers) + except ValueError: + numbers.clear() + elif n == 49: + bg = None + else: + pass # Unknown codes are ignored + return ''.join(out) + + +def _get_extended_color(numbers): + n = numbers.pop(0) + if n == 2 and len(numbers) >= 3: + # 24-bit RGB + r = numbers.pop(0) + g = numbers.pop(0) + b = numbers.pop(0) + if not all(0 <= c <= 255 for c in (r, g, b)): + raise ValueError() + elif n == 5 and len(numbers) >= 1: + # 256 colors + idx = numbers.pop(0) + if idx < 0: + raise ValueError() + elif idx < 16: + # 8 default terminal colors + return idx % 8 # ignore bright/non-bright distinction + elif idx < 232: + # 6x6x6 color cube, see http://stackoverflow.com/a/27165165/500098 + r = (idx - 16) // 36 + r = 55 + r * 40 if r > 0 else 0 + g = ((idx - 16) % 36) // 6 + g = 55 + g * 40 if g > 0 else 0 + b = (idx - 16) % 6 + b = 55 + b * 40 if b > 0 else 0 + elif idx < 256: + # grayscale, see http://stackoverflow.com/a/27165165/500098 + r = g = b = (idx - 232) * 10 + 8 + else: + raise ValueError() + else: + raise ValueError() + return r, g, b diff --git a/nbconvert/templates/latex/base.tplx b/nbconvert/templates/latex/base.tplx index 81158fb56..c70cd597d 100644 --- a/nbconvert/templates/latex/base.tplx +++ b/nbconvert/templates/latex/base.tplx @@ -63,6 +63,10 @@ This template does not define a docclass, the inheriting class must define this. \definecolor{lightblue}{rgb}{0.53,0.81,0.92} \definecolor{lightpurple}{rgb}{0.87,0.63,0.87} \definecolor{lightcyan}{rgb}{0.5,1.0,0.83} + + % commands for extended ansi colors (commas behave strangely in fancyvrb) + \newcommand{\textcolorRGB}[3]{\textcolor[RGB]{#1,#2,#3}} + \newcommand{\colorboxRGB}[3]{\colorbox[RGB]{#1,#2,#3}} % commands and environments needed by pandoc snippets % extracted from the output of `pandoc -s` diff --git a/nbconvert/utils/coloransi.py b/nbconvert/utils/coloransi.py deleted file mode 100644 index 06ede5ead..000000000 --- a/nbconvert/utils/coloransi.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -"""Tools for coloring text in ANSI terminals. -""" - -# subset of IPython.utils.ansicolors, which is: -#***************************************************************************** -# Copyright (C) 2002-2006 Fernando Perez. -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -__all__ = ['TermColors'] - - -color_templates = ( - # Dark colors - ("Black" , "0;30"), - ("Red" , "0;31"), - ("Green" , "0;32"), - ("Brown" , "0;33"), - ("Blue" , "0;34"), - ("Purple" , "0;35"), - ("Cyan" , "0;36"), - ("LightGray" , "0;37"), - # Light colors - ("DarkGray" , "1;30"), - ("LightRed" , "1;31"), - ("LightGreen" , "1;32"), - ("Yellow" , "1;33"), - ("LightBlue" , "1;34"), - ("LightPurple" , "1;35"), - ("LightCyan" , "1;36"), - ("White" , "1;37"), - # Blinking colors. Probably should not be used in anything serious. - ("BlinkBlack" , "5;30"), - ("BlinkRed" , "5;31"), - ("BlinkGreen" , "5;32"), - ("BlinkYellow" , "5;33"), - ("BlinkBlue" , "5;34"), - ("BlinkPurple" , "5;35"), - ("BlinkCyan" , "5;36"), - ("BlinkLightGray", "5;37"), - ) - -def make_color_table(in_class): - """Build a set of color attributes in a class. - - Helper function for building the :class:`TermColors` and - :class`InputTermColors`. - """ - for name,value in color_templates: - setattr(in_class,name,in_class._base % value) - -class TermColors: - """Color escape sequences. - - This class defines the escape sequences for all the standard (ANSI?) - colors in terminals. Also defines a NoColor escape which is just the null - string, suitable for defining 'dummy' color schemes in terminals which get - confused by color escapes. - - This class should be used as a mixin for building color schemes.""" - - NoColor = '' # for color schemes in color-less terminals. - Normal = '\033[0m' # Reset normal coloring - _base = '\033[%sm' # Template for all other colors - -# Build the actual color table as a set of class attributes: -make_color_table(TermColors) From 5e61dbba7f7b7ca8d55ab9a0f6bbc815f482682e Mon Sep 17 00:00:00 2001 From: Matthias Geier Date: Wed, 3 Feb 2016 21:17:44 +0100 Subject: [PATCH 2/6] Move strip_ansi() from ipython_genutils and change regex --- nbconvert/filters/ansi.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/nbconvert/filters/ansi.py b/nbconvert/filters/ansi.py index 247dd9a6c..bea058134 100644 --- a/nbconvert/filters/ansi.py +++ b/nbconvert/filters/ansi.py @@ -5,7 +5,6 @@ import re import jinja2 -from ipython_genutils.text import strip_ansi __all__ = [ 'strip_ansi', @@ -13,6 +12,8 @@ 'ansi2latex' ] +_ANSI_RE = re.compile('\x1b\\[(.*?)([@-~])') + _FG_HTML = ( 'ansiblack', 'ansired', @@ -58,6 +59,19 @@ ) +def strip_ansi(source): + """ + Remove ANSI escape codes from text. + + Parameters + ---------- + source : str + Source to remove the ANSI from + + """ + return _ANSI_RE.sub('', source) + + def ansi2html(text): """ Convert ANSI colors to HTML colors. @@ -170,14 +184,13 @@ def _ansi2anything(text, converter): fixConsole() in notebook/notebook/static/base/js/utils.js. """ - ansi_re = re.compile('\x1b\\[([^@-~]*)([@-~])') fg, bg = None, None bold = False numbers = [] out = [] while text: - m = ansi_re.search(text) + m = _ANSI_RE.search(text) if m: if m.group(2) == 'm': try: From 409398f4bd46c43bc802621f45b3e5c59ab94fcd Mon Sep 17 00:00:00 2001 From: Matthias Geier Date: Wed, 3 Feb 2016 21:18:07 +0100 Subject: [PATCH 3/6] Fix ANSI tests --- nbconvert/filters/tests/test_ansi.py | 61 +++++++++++++--------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/nbconvert/filters/tests/test_ansi.py b/nbconvert/filters/tests/test_ansi.py index b4c6f611c..656f8542e 100644 --- a/nbconvert/filters/tests/test_ansi.py +++ b/nbconvert/filters/tests/test_ansi.py @@ -5,8 +5,6 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. -from nbconvert.utils.coloransi import TermColors - from ...tests.base import TestsBase from ..ansi import strip_ansi, ansi2html, ansi2latex @@ -17,60 +15,57 @@ class TestAnsi(TestsBase): def test_strip_ansi(self): """strip_ansi test""" correct_outputs = { - '%s%s%s' % (TermColors.Green, TermColors.White, TermColors.Red) : '', - 'hello%s' % TermColors.Blue: 'hello', - 'he%s%sllo' % (TermColors.Yellow, TermColors.Cyan) : 'hello', - '%shello' % TermColors.Blue : 'hello', - '{0}h{0}e{0}l{0}l{0}o{0}'.format(TermColors.Red) : 'hello', - 'hel%slo' % TermColors.Green : 'hello', - 'hello' : 'hello'} + '\x1b[32m\x1b[1m\x1b[0;44m\x1b[38;2;255;0;255m\x1b[;m\x1b[m': '', + 'hello\x1b[000;34m': 'hello', + 'he\x1b[1;33m\x1b[;36mllo': 'hello', + '\x1b[;34mhello': 'hello', + '\x1b[31mh\x1b[31me\x1b[31ml\x1b[31ml\x1b[31mo\x1b[31m': 'hello', + 'hel\x1b[;00;;032;;;32mlo': 'hello', + 'hello': 'hello', + } for inval, outval in correct_outputs.items(): self._try_strip_ansi(inval, outval) - def _try_strip_ansi(self, inval, outval): self.assertEqual(outval, strip_ansi(inval)) - def test_ansi2html(self): """ansi2html test""" correct_outputs = { - '%s' % (TermColors.Red) : '', - 'hello%s' % TermColors.Blue: 'hello', - 'he%s%sllo' % (TermColors.Green, TermColors.Cyan) : 'hello', - '%shello' % TermColors.Yellow : 'hello', - '{0}h{0}e{0}l{0}l{0}o{0}'.format(TermColors.White) : 'hello', - 'hel%slo' % TermColors.Green : 'hello', - 'hello' : 'hello'} + '\x1b[31m': '', + 'hello\x1b[34m': 'hello', + 'he\x1b[32m\x1b[36mllo': 'hello', + '\x1b[1;33mhello': 'hello', + '\x1b[37mh\x1b[0;037me\x1b[;0037ml\x1b[00;37ml\x1b[;;37mo': 'hello', + 'hel\x1b[0;32mlo': 'hello', + 'hello': 'hello', + } for inval, outval in correct_outputs.items(): self._try_ansi2html(inval, outval) - def _try_ansi2html(self, inval, outval): self.fuzzy_compare(outval, ansi2html(inval)) - def test_ansi2latex(self): """ansi2latex test""" correct_outputs = { - '%s' % (TermColors.Red) : r'{\color{red}}', - 'hello%s' % TermColors.Blue: r'hello{\color{blue}}', - 'he%s%sllo' % (TermColors.Green, TermColors.Cyan) : r'he{\color{green}}{\color{cyan}llo}', - '%shello' % TermColors.Yellow : r'\textbf{\color{yellow}hello}', - '{0}h{0}e{0}l{0}l{0}o{0}'.format(TermColors.White) : r'\textbf{\color{white}h}\textbf{\color{white}e}\textbf{\color{white}l}\textbf{\color{white}l}\textbf{\color{white}o}\textbf{\color{white}}', - 'hel%slo' % TermColors.Green : r'hel{\color{green}lo}', - 'hello' : 'hello', - u'hello\x1b[34mthere\x1b[mworld' : u'hello{\\color{blue}there}world', - u'hello\x1b[mthere': u'hellothere', - u'hello\x1b[01;34mthere' : u"hello\\textbf{\\color{lightblue}there}", - u'hello\x1b[001;34mthere' : u"hello\\textbf{\\color{lightblue}there}" - } + '\x1b[31m': '', + 'hello\x1b[34m': 'hello', + 'he\x1b[32m\x1b[36mllo': r'he\textcolor{cyan}{llo}', + '\x1b[1;33mhello': r'\textcolor{brown}{\textbf{hello}}', + '\x1b[37mh\x1b[0;037me\x1b[;0037ml\x1b[00;37ml\x1b[;;37mo': r'\textcolor{lightgray}{h}\textcolor{lightgray}{e}\textcolor{lightgray}{l}\textcolor{lightgray}{l}\textcolor{lightgray}{o}', + 'hel\x1b[0;32mlo': r'hel\textcolor{green}{lo}', + 'hello': 'hello', + 'hello\x1b[34mthere\x1b[mworld': r'hello\textcolor{blue}{there}world', + 'hello\x1b[mthere': 'hellothere', + 'hello\x1b[01;34mthere': r'hello\textcolor{blue}{\textbf{there}}', + 'hello\x1b[001;34mthere': r'hello\textcolor{blue}{\textbf{there}}', + } for inval, outval in correct_outputs.items(): self._try_ansi2latex(inval, outval) - def _try_ansi2latex(self, inval, outval): self.fuzzy_compare(outval, ansi2latex(inval), case_sensitive=True) From 825474b53dc93b08bb2890c4d83ad3ff233723f6 Mon Sep 17 00:00:00 2001 From: Matthias Geier Date: Thu, 4 Feb 2016 17:42:46 +0100 Subject: [PATCH 4/6] Avoid \newcommand for ANSI RGB colors --- nbconvert/filters/ansi.py | 9 +++++++-- nbconvert/templates/latex/base.tplx | 4 ---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/nbconvert/filters/ansi.py b/nbconvert/filters/ansi.py index bea058134..4dfd0bc5b 100644 --- a/nbconvert/filters/ansi.py +++ b/nbconvert/filters/ansi.py @@ -146,7 +146,9 @@ def _latexconverter(fg, bg, bold): starttag += r'\textcolor{' + _FG_LATEX[fg] + '}{' endtag = '}' + endtag elif fg: - starttag += r'\textcolorRGB{%s}{%s}{%s}{' % fg + # See http://tex.stackexchange.com/a/291102/13684 + starttag += r'\def\tcRGB{\textcolor[RGB]}\expandafter' + starttag += r'\tcRGB\expandafter{\detokenize{%s,%s,%s}}{' % fg endtag = '}' + endtag if isinstance(bg, int): @@ -154,7 +156,10 @@ def _latexconverter(fg, bg, bold): starttag += _BG_LATEX[bg] + '}{' endtag = r'\strut}' + endtag elif bg: - starttag += r'\setlength{\fboxsep}{0pt}\colorboxRGB{%s}{%s}{%s}{' % bg + starttag += r'\setlength{\fboxsep}{0pt}' + # See http://tex.stackexchange.com/a/291102/13684 + starttag += r'\def\cbRGB{\colorbox[RGB]}\expandafter' + starttag += r'\cbRGB\expandafter{\detokenize{%s,%s,%s}}{' % bg endtag = r'\strut}' + endtag if bold: diff --git a/nbconvert/templates/latex/base.tplx b/nbconvert/templates/latex/base.tplx index c70cd597d..602d58ccc 100644 --- a/nbconvert/templates/latex/base.tplx +++ b/nbconvert/templates/latex/base.tplx @@ -64,10 +64,6 @@ This template does not define a docclass, the inheriting class must define this. \definecolor{lightpurple}{rgb}{0.87,0.63,0.87} \definecolor{lightcyan}{rgb}{0.5,1.0,0.83} - % commands for extended ansi colors (commas behave strangely in fancyvrb) - \newcommand{\textcolorRGB}[3]{\textcolor[RGB]{#1,#2,#3}} - \newcommand{\colorboxRGB}[3]{\colorbox[RGB]{#1,#2,#3}} - % commands and environments needed by pandoc snippets % extracted from the output of `pandoc -s` \providecommand{\tightlist}{% From f46896522b14becd95ee67f8dba6717a5ea7259e Mon Sep 17 00:00:00 2001 From: Matthias Geier Date: Sat, 13 Feb 2016 17:50:41 +0100 Subject: [PATCH 5/6] Remove apparently useless boilerplate from ANSI tests --- nbconvert/filters/tests/test_ansi.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/nbconvert/filters/tests/test_ansi.py b/nbconvert/filters/tests/test_ansi.py index 656f8542e..45d30cf21 100644 --- a/nbconvert/filters/tests/test_ansi.py +++ b/nbconvert/filters/tests/test_ansi.py @@ -25,10 +25,7 @@ def test_strip_ansi(self): } for inval, outval in correct_outputs.items(): - self._try_strip_ansi(inval, outval) - - def _try_strip_ansi(self, inval, outval): - self.assertEqual(outval, strip_ansi(inval)) + self.assertEqual(outval, strip_ansi(inval)) def test_ansi2html(self): """ansi2html test""" @@ -43,10 +40,7 @@ def test_ansi2html(self): } for inval, outval in correct_outputs.items(): - self._try_ansi2html(inval, outval) - - def _try_ansi2html(self, inval, outval): - self.fuzzy_compare(outval, ansi2html(inval)) + self.fuzzy_compare(outval, ansi2html(inval)) def test_ansi2latex(self): """ansi2latex test""" @@ -65,7 +59,4 @@ def test_ansi2latex(self): } for inval, outval in correct_outputs.items(): - self._try_ansi2latex(inval, outval) - - def _try_ansi2latex(self, inval, outval): - self.fuzzy_compare(outval, ansi2latex(inval), case_sensitive=True) + self.fuzzy_compare(outval, ansi2latex(inval), case_sensitive=True) From cbd76bd48f0b9d67ff5fa59c95691011cb3a1f69 Mon Sep 17 00:00:00 2001 From: Matthias Geier Date: Sun, 14 Feb 2016 10:04:58 +0100 Subject: [PATCH 6/6] ANSI tests: add test case for overlapping formatting --- nbconvert/filters/tests/test_ansi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nbconvert/filters/tests/test_ansi.py b/nbconvert/filters/tests/test_ansi.py index 45d30cf21..701f50bd7 100644 --- a/nbconvert/filters/tests/test_ansi.py +++ b/nbconvert/filters/tests/test_ansi.py @@ -37,6 +37,7 @@ def test_ansi2html(self): '\x1b[37mh\x1b[0;037me\x1b[;0037ml\x1b[00;37ml\x1b[;;37mo': 'hello', 'hel\x1b[0;32mlo': 'hello', 'hello': 'hello', + '\x1b[1mhello\x1b[33mworld\x1b[0m': 'helloworld', } for inval, outval in correct_outputs.items(): @@ -56,6 +57,7 @@ def test_ansi2latex(self): 'hello\x1b[mthere': 'hellothere', 'hello\x1b[01;34mthere': r'hello\textcolor{blue}{\textbf{there}}', 'hello\x1b[001;34mthere': r'hello\textcolor{blue}{\textbf{there}}', + '\x1b[1mhello\x1b[33mworld\x1b[0m': r'\textbf{hello}\textcolor{brown}{\textbf{world}}', } for inval, outval in correct_outputs.items():