1 | """reST directive for syntax-highlighting ipython interactive sessions. |
---|
2 | |
---|
3 | XXX - See what improvements can be made based on the new (as of Sept 2009) |
---|
4 | 'pycon' lexer for the python console. At the very least it will give better |
---|
5 | highlighted tracebacks. |
---|
6 | """ |
---|
7 | |
---|
8 | #----------------------------------------------------------------------------- |
---|
9 | # Needed modules |
---|
10 | |
---|
11 | # Standard library |
---|
12 | import re |
---|
13 | |
---|
14 | # Third party |
---|
15 | from pygments.lexer import Lexer, do_insertions |
---|
16 | from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, |
---|
17 | PythonTracebackLexer) |
---|
18 | from pygments.token import Comment, Generic |
---|
19 | |
---|
20 | from sphinx import highlighting |
---|
21 | |
---|
22 | #----------------------------------------------------------------------------- |
---|
23 | # Global constants |
---|
24 | line_re = re.compile('.*?\n') |
---|
25 | |
---|
26 | #----------------------------------------------------------------------------- |
---|
27 | # Code begins - classes and functions |
---|
28 | |
---|
29 | class IPythonConsoleLexer(Lexer): |
---|
30 | """ |
---|
31 | For IPython console output or doctests, such as: |
---|
32 | |
---|
33 | .. sourcecode:: ipython |
---|
34 | |
---|
35 | In [1]: a = 'foo' |
---|
36 | |
---|
37 | In [2]: a |
---|
38 | Out[2]: 'foo' |
---|
39 | |
---|
40 | In [3]: print a |
---|
41 | foo |
---|
42 | |
---|
43 | In [4]: 1 / 0 |
---|
44 | |
---|
45 | Notes: |
---|
46 | |
---|
47 | - Tracebacks are not currently supported. |
---|
48 | |
---|
49 | - It assumes the default IPython prompts, not customized ones. |
---|
50 | """ |
---|
51 | |
---|
52 | name = 'IPython console session' |
---|
53 | aliases = ['ipython'] |
---|
54 | mimetypes = ['text/x-ipython-console'] |
---|
55 | input_prompt = re.compile("(In \[[0-9]+\]: )|( \.\.\.+:)") |
---|
56 | output_prompt = re.compile("(Out\[[0-9]+\]: )|( \.\.\.+:)") |
---|
57 | continue_prompt = re.compile(" \.\.\.+:") |
---|
58 | tb_start = re.compile("\-+") |
---|
59 | |
---|
60 | def get_tokens_unprocessed(self, text): |
---|
61 | pylexer = PythonLexer(**self.options) |
---|
62 | tblexer = PythonTracebackLexer(**self.options) |
---|
63 | |
---|
64 | curcode = '' |
---|
65 | insertions = [] |
---|
66 | for match in line_re.finditer(text): |
---|
67 | line = match.group() |
---|
68 | input_prompt = self.input_prompt.match(line) |
---|
69 | continue_prompt = self.continue_prompt.match(line.rstrip()) |
---|
70 | output_prompt = self.output_prompt.match(line) |
---|
71 | if line.startswith("#"): |
---|
72 | insertions.append((len(curcode), |
---|
73 | [(0, Comment, line)])) |
---|
74 | elif input_prompt is not None: |
---|
75 | insertions.append((len(curcode), |
---|
76 | [(0, Generic.Prompt, input_prompt.group())])) |
---|
77 | curcode += line[input_prompt.end():] |
---|
78 | elif continue_prompt is not None: |
---|
79 | insertions.append((len(curcode), |
---|
80 | [(0, Generic.Prompt, continue_prompt.group())])) |
---|
81 | curcode += line[continue_prompt.end():] |
---|
82 | elif output_prompt is not None: |
---|
83 | # Use the 'error' token for output. We should probably make |
---|
84 | # our own token, but error is typicaly in a bright color like |
---|
85 | # red, so it works fine for our output prompts. |
---|
86 | insertions.append((len(curcode), |
---|
87 | [(0, Generic.Error, output_prompt.group())])) |
---|
88 | curcode += line[output_prompt.end():] |
---|
89 | else: |
---|
90 | if curcode: |
---|
91 | for item in do_insertions(insertions, |
---|
92 | pylexer.get_tokens_unprocessed(curcode)): |
---|
93 | yield item |
---|
94 | curcode = '' |
---|
95 | insertions = [] |
---|
96 | yield match.start(), Generic.Output, line |
---|
97 | if curcode: |
---|
98 | for item in do_insertions(insertions, |
---|
99 | pylexer.get_tokens_unprocessed(curcode)): |
---|
100 | yield item |
---|
101 | |
---|
102 | |
---|
103 | def setup(app): |
---|
104 | """Setup as a sphinx extension.""" |
---|
105 | |
---|
106 | # This is only a lexer, so adding it below to pygments appears sufficient. |
---|
107 | # But if somebody knows that the right API usage should be to do that via |
---|
108 | # sphinx, by all means fix it here. At least having this setup.py |
---|
109 | # suppresses the sphinx warning we'd get without it. |
---|
110 | pass |
---|
111 | |
---|
112 | #----------------------------------------------------------------------------- |
---|
113 | # Register the extension as a valid pygments lexer |
---|
114 | highlighting.lexers['ipython'] = IPythonConsoleLexer() |
---|