##// END OF EJS Templates
Use int style instead of string....
Richard Everson -
Show More
@@ -1,177 +1,177 b''
1 """Filters for processing ANSI colors within Jinja templates.
1 """Filters for processing ANSI colors within Jinja templates.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2013, the IPython Development Team.
4 # Copyright (c) 2013, the IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import re
15 import re
16 from IPython.utils import coloransi
16 from IPython.utils import coloransi
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Classes and functions
19 # Classes and functions
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 __all__ = [
22 __all__ = [
23 'strip_ansi',
23 'strip_ansi',
24 'ansi2html',
24 'ansi2html',
25 'single_ansi2latex',
25 'single_ansi2latex',
26 'ansi2latex'
26 'ansi2latex'
27 ]
27 ]
28
28
29 def strip_ansi(source):
29 def strip_ansi(source):
30 """
30 """
31 Remove ansi from text
31 Remove ansi from text
32
32
33 Parameters
33 Parameters
34 ----------
34 ----------
35 source : str
35 source : str
36 Source to remove the ansi from
36 Source to remove the ansi from
37 """
37 """
38
38
39 return re.sub(r'\033\[(\d|;)+?m', '', source)
39 return re.sub(r'\033\[(\d|;)+?m', '', source)
40
40
41
41
42 def ansi2html(text):
42 def ansi2html(text):
43 """
43 """
44 Conver ansi colors to html colors.
44 Conver ansi colors to html colors.
45
45
46 Parameters
46 Parameters
47 ----------
47 ----------
48 text : str
48 text : str
49 Text containing ansi colors to convert to html
49 Text containing ansi colors to convert to html
50 """
50 """
51
51
52 ansi_colormap = {
52 ansi_colormap = {
53 '30': 'ansiblack',
53 '30': 'ansiblack',
54 '31': 'ansired',
54 '31': 'ansired',
55 '32': 'ansigreen',
55 '32': 'ansigreen',
56 '33': 'ansiyellow',
56 '33': 'ansiyellow',
57 '34': 'ansiblue',
57 '34': 'ansiblue',
58 '35': 'ansipurple',
58 '35': 'ansipurple',
59 '36': 'ansicyan',
59 '36': 'ansicyan',
60 '37': 'ansigrey',
60 '37': 'ansigrey',
61 '01': 'ansibold',
61 '01': 'ansibold',
62 }
62 }
63
63
64 # do ampersand first
64 # do ampersand first
65 text = text.replace('&', '&')
65 text = text.replace('&', '&')
66 html_escapes = {
66 html_escapes = {
67 '<': '&lt;',
67 '<': '&lt;',
68 '>': '&gt;',
68 '>': '&gt;',
69 "'": '&apos;',
69 "'": '&apos;',
70 '"': '&quot;',
70 '"': '&quot;',
71 '`': '&#96;',
71 '`': '&#96;',
72 }
72 }
73
73
74 for c, escape in html_escapes.items():
74 for c, escape in html_escapes.items():
75 text = text.replace(c, escape)
75 text = text.replace(c, escape)
76
76
77 ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m')
77 ansi_re = re.compile('\x1b' + r'\[([\dA-Fa-f;]*?)m')
78 m = ansi_re.search(text)
78 m = ansi_re.search(text)
79 opened = False
79 opened = False
80 cmds = []
80 cmds = []
81 opener = ''
81 opener = ''
82 closer = ''
82 closer = ''
83 while m:
83 while m:
84 cmds = m.groups()[0].split(';')
84 cmds = m.groups()[0].split(';')
85 closer = '</span>' if opened else ''
85 closer = '</span>' if opened else ''
86
86
87 # True if there is there more than one element in cmds, *or*
87 # True if there is there more than one element in cmds, *or*
88 # if there is only one but it is not equal to a string of zeroes.
88 # if there is only one but it is not equal to a string of zeroes.
89 opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0])
89 opened = len(cmds) > 1 or cmds[0] != '0' * len(cmds[0])
90 classes = []
90 classes = []
91 for cmd in cmds:
91 for cmd in cmds:
92 if cmd in ansi_colormap:
92 if cmd in ansi_colormap:
93 classes.append(ansi_colormap.get(cmd))
93 classes.append(ansi_colormap.get(cmd))
94
94
95 if classes:
95 if classes:
96 opener = '<span class="%s">' % (' '.join(classes))
96 opener = '<span class="%s">' % (' '.join(classes))
97 else:
97 else:
98 opener = ''
98 opener = ''
99 text = re.sub(ansi_re, closer + opener, text, 1)
99 text = re.sub(ansi_re, closer + opener, text, 1)
100
100
101 m = ansi_re.search(text)
101 m = ansi_re.search(text)
102
102
103 if opened:
103 if opened:
104 text += '</span>'
104 text += '</span>'
105 return text
105 return text
106
106
107
107
108 def single_ansi2latex(code):
108 def single_ansi2latex(code):
109 """Converts single ansi markup to latex format.
109 """Converts single ansi markup to latex format.
110
110
111 Return latex code and number of open brackets.
111 Return latex code and number of open brackets.
112
112
113 Accepts codes like '\x1b[1;32m' (bold, red) and the short form '\x1b[32m' (red)
113 Accepts codes like '\x1b[1;32m' (bold, red) and the short form '\x1b[32m' (red)
114
114
115 Colors are matched to those defined in coloransi, which defines colors
115 Colors are matched to those defined in coloransi, which defines colors
116 using the 0, 1 (bold) and 5 (blinking) styles. Styles 1 and 5 are
116 using the 0, 1 (bold) and 5 (blinking) styles. Styles 1 and 5 are
117 interpreted as bold. All other styles are mapped to 0. Note that in
117 interpreted as bold. All other styles are mapped to 0. Note that in
118 coloransi, a style of 1 does not just mean bold; for example, Brown is
118 coloransi, a style of 1 does not just mean bold; for example, Brown is
119 "0;33", but Yellow is "1;33". An empty string is returned for unrecognised
119 "0;33", but Yellow is "1;33". An empty string is returned for unrecognised
120 codes and the "reset" code '\x1b[m'.
120 codes and the "reset" code '\x1b[m'.
121 """
121 """
122 components = code.split(';')
122 components = code.split(';')
123 if len(components) > 1:
123 if len(components) > 1:
124 style = components[0][-1]
124 # Style is digits after '['
125 style = int(components[0].split('[')[-1])
125 color = components[1][:-1]
126 color = components[1][:-1]
126 else:
127 else:
127 style = '0'
128 style = 0
128 color = components[0][-3:-1]
129 color = components[0][-3:-1]
129
130
130 # If the style is not normal (0), bold (1) or blinking (5) then treat it as normal
131 # If the style is not normal (0), bold (1) or blinking (5) then treat it as normal
131 if style not in '015':
132 if style not in [0, 1, 5]:
132 style = '0'
133 style = 0
133
134
134 for name, tcode in coloransi.color_templates:
135 for name, tcode in coloransi.color_templates:
135 tstyle, tcolor = tcode.split(';')
136 tstyle, tcolor = tcode.split(';')
137 tstyle = int(tstyle)
136 if tstyle == style and tcolor == color:
138 if tstyle == style and tcolor == color:
137 break
139 break
138 else:
140 else:
139 return '', 0
141 return '', 0
140
142
141 if style == '5':
143 if style == 5:
142 name = name[5:] # BlinkRed -> Red, etc
144 name = name[5:] # BlinkRed -> Red, etc
143 name = name.lower()
145 name = name.lower()
144
146
145 if style in '15':
147 if style in [1, 5]:
146 return r'\textbf{\color{'+name+'}', 1
148 return r'\textbf{\color{'+name+'}', 1
147 else:
149 else:
148 return r'{\color{'+name+'}', 1
150 return r'{\color{'+name+'}', 1
149
151
150 def ansi2latex(text):
152 def ansi2latex(text):
151 """Converts ansi formated text to latex version
153 """Converts ansi formated text to latex version
152
154
153 based on https://bitbucket.org/birkenfeld/sphinx-contrib/ansi.py
155 based on https://bitbucket.org/birkenfeld/sphinx-contrib/ansi.py
154 """
156 """
155 color_pattern = re.compile('\x1b\\[([^m]*)m')
157 color_pattern = re.compile('\x1b\\[([^m]*)m')
156 last_end = 0
158 last_end = 0
157 openbrack = 0
159 openbrack = 0
158 outstring = ''
160 outstring = ''
159 for match in color_pattern.finditer(text):
161 for match in color_pattern.finditer(text):
160 head = text[last_end:match.start()]
162 head = text[last_end:match.start()]
161 outstring += head
163 outstring += head
162 if openbrack:
164 if openbrack:
163 outstring += '}'*openbrack
165 outstring += '}'*openbrack
164 openbrack = 0
166 openbrack = 0
165 code = match.group()
167 code = match.group()
166 if not (code == coloransi.TermColors.Normal or openbrack):
168 if not (code == coloransi.TermColors.Normal or openbrack):
167 texform, openbrack = single_ansi2latex(code)
169 texform, openbrack = single_ansi2latex(code)
168 outstring += texform
170 outstring += texform
169 last_end = match.end()
171 last_end = match.end()
170
172
171 # Add the remainer of the string and THEN close any remaining color brackets.
173 # Add the remainer of the string and THEN close any remaining color brackets.
172 outstring += text[last_end:]
174 outstring += text[last_end:]
173 if openbrack:
175 if openbrack:
174 outstring += '}'*openbrack
176 outstring += '}'*openbrack
175 return outstring.strip()
177 return outstring.strip()
176
177
@@ -1,87 +1,89 b''
1 """
1 """
2 Module with tests for ansi filters
2 Module with tests for ansi filters
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 from IPython.utils.coloransi import TermColors
17 from IPython.utils.coloransi import TermColors
18
18
19 from ...tests.base import TestsBase
19 from ...tests.base import TestsBase
20 from ..ansi import strip_ansi, ansi2html, ansi2latex
20 from ..ansi import strip_ansi, ansi2html, ansi2latex
21
21
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Class
24 # Class
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 class TestAnsi(TestsBase):
27 class TestAnsi(TestsBase):
28 """Contains test functions for ansi.py"""
28 """Contains test functions for ansi.py"""
29
29
30 def test_strip_ansi(self):
30 def test_strip_ansi(self):
31 """strip_ansi test"""
31 """strip_ansi test"""
32 correct_outputs = {
32 correct_outputs = {
33 '%s%s%s' % (TermColors.Green, TermColors.White, TermColors.Red) : '',
33 '%s%s%s' % (TermColors.Green, TermColors.White, TermColors.Red) : '',
34 'hello%s' % TermColors.Blue: 'hello',
34 'hello%s' % TermColors.Blue: 'hello',
35 'he%s%sllo' % (TermColors.Yellow, TermColors.Cyan) : 'hello',
35 'he%s%sllo' % (TermColors.Yellow, TermColors.Cyan) : 'hello',
36 '%shello' % TermColors.Blue : 'hello',
36 '%shello' % TermColors.Blue : 'hello',
37 '{0}h{0}e{0}l{0}l{0}o{0}'.format(TermColors.Red) : 'hello',
37 '{0}h{0}e{0}l{0}l{0}o{0}'.format(TermColors.Red) : 'hello',
38 'hel%slo' % TermColors.Green : 'hello',
38 'hel%slo' % TermColors.Green : 'hello',
39 'hello' : 'hello'}
39 'hello' : 'hello'}
40
40
41 for inval, outval in correct_outputs.items():
41 for inval, outval in correct_outputs.items():
42 self._try_strip_ansi(inval, outval)
42 self._try_strip_ansi(inval, outval)
43
43
44
44
45 def _try_strip_ansi(self, inval, outval):
45 def _try_strip_ansi(self, inval, outval):
46 self.assertEqual(outval, strip_ansi(inval))
46 self.assertEqual(outval, strip_ansi(inval))
47
47
48
48
49 def test_ansi2html(self):
49 def test_ansi2html(self):
50 """ansi2html test"""
50 """ansi2html test"""
51 correct_outputs = {
51 correct_outputs = {
52 '%s' % (TermColors.Red) : '<span class="ansired"></span>',
52 '%s' % (TermColors.Red) : '<span class="ansired"></span>',
53 'hello%s' % TermColors.Blue: 'hello<span class="ansiblue"></span>',
53 'hello%s' % TermColors.Blue: 'hello<span class="ansiblue"></span>',
54 'he%s%sllo' % (TermColors.Green, TermColors.Cyan) : 'he<span class="ansigreen"></span><span class="ansicyan">llo</span>',
54 'he%s%sllo' % (TermColors.Green, TermColors.Cyan) : 'he<span class="ansigreen"></span><span class="ansicyan">llo</span>',
55 '%shello' % TermColors.Yellow : '<span class="ansiyellow">hello</span>',
55 '%shello' % TermColors.Yellow : '<span class="ansiyellow">hello</span>',
56 '{0}h{0}e{0}l{0}l{0}o{0}'.format(TermColors.White) : '<span class="ansigrey">h</span><span class="ansigrey">e</span><span class="ansigrey">l</span><span class="ansigrey">l</span><span class="ansigrey">o</span><span class="ansigrey"></span>',
56 '{0}h{0}e{0}l{0}l{0}o{0}'.format(TermColors.White) : '<span class="ansigrey">h</span><span class="ansigrey">e</span><span class="ansigrey">l</span><span class="ansigrey">l</span><span class="ansigrey">o</span><span class="ansigrey"></span>',
57 'hel%slo' % TermColors.Green : 'hel<span class="ansigreen">lo</span>',
57 'hel%slo' % TermColors.Green : 'hel<span class="ansigreen">lo</span>',
58 'hello' : 'hello'}
58 'hello' : 'hello'}
59
59
60 for inval, outval in correct_outputs.items():
60 for inval, outval in correct_outputs.items():
61 self._try_ansi2html(inval, outval)
61 self._try_ansi2html(inval, outval)
62
62
63
63
64 def _try_ansi2html(self, inval, outval):
64 def _try_ansi2html(self, inval, outval):
65 self.fuzzy_compare(outval, ansi2html(inval))
65 self.fuzzy_compare(outval, ansi2html(inval))
66
66
67
67
68 def test_ansi2latex(self):
68 def test_ansi2latex(self):
69 """ansi2latex test"""
69 """ansi2latex test"""
70 correct_outputs = {
70 correct_outputs = {
71 '%s' % (TermColors.Red) : r'{\color{red}}',
71 '%s' % (TermColors.Red) : r'{\color{red}}',
72 'hello%s' % TermColors.Blue: r'hello{\color{blue}}',
72 'hello%s' % TermColors.Blue: r'hello{\color{blue}}',
73 'he%s%sllo' % (TermColors.Green, TermColors.Cyan) : r'he{\color{green}}{\color{cyan}llo}',
73 'he%s%sllo' % (TermColors.Green, TermColors.Cyan) : r'he{\color{green}}{\color{cyan}llo}',
74 '%shello' % TermColors.Yellow : r'\textbf{\color{yellow}hello}',
74 '%shello' % TermColors.Yellow : r'\textbf{\color{yellow}hello}',
75 '{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}}',
75 '{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}}',
76 'hel%slo' % TermColors.Green : r'hel{\color{green}lo}',
76 'hel%slo' % TermColors.Green : r'hel{\color{green}lo}',
77 'hello' : 'hello',
77 'hello' : 'hello',
78 u'hello\x1b[34mthere\x1b[mworld' : u'hello{\\color{blue}there}world',
78 u'hello\x1b[34mthere\x1b[mworld' : u'hello{\\color{blue}there}world',
79 u'hello\x1b[mthere': u'hellothere'
79 u'hello\x1b[mthere': u'hellothere',
80 u'hello\x1b[01;34mthere' : u"hello\\textbf{\\color{lightblue}there}",
81 u'hello\x1b[001;34mthere' : u"hello\\textbf{\\color{lightblue}there}"
80 }
82 }
81
83
82 for inval, outval in correct_outputs.items():
84 for inval, outval in correct_outputs.items():
83 self._try_ansi2latex(inval, outval)
85 self._try_ansi2latex(inval, outval)
84
86
85
87
86 def _try_ansi2latex(self, inval, outval):
88 def _try_ansi2latex(self, inval, outval):
87 self.fuzzy_compare(outval, ansi2latex(inval), case_sensitive=True)
89 self.fuzzy_compare(outval, ansi2latex(inval), case_sensitive=True)
General Comments 0
You need to be logged in to leave comments. Login now