Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/doc/common/custom-sphinx-build.py
8815 views
1
"""
2
This is Sage's version of the sphinx-build script
3
4
Enhancements are:
5
6
* import the Sage library to access the docstrings, otherwise doc
7
buliding doesn't work.
8
9
* redirect stdout to our own logger, and remove some unwanted chatter.
10
"""
11
12
# override the fancy multi-line formatting
13
def term_width_line(text):
14
return text + '\n'
15
16
sphinx.util.console.term_width_line = term_width_line
17
18
19
# useless_chatter: regular expressions to be filtered from Sphinx
20
# output.
21
22
useless_chatter = (
23
re.compile('^$'),
24
re.compile('^Running Sphinx v'),
25
re.compile('^loading intersphinx inventory from '),
26
re.compile('^Compiling a sub-document'),
27
re.compile('^updating environment: 0 added, 0 changed, 0 removed'),
28
re.compile('^looking for now-outdated files... none found'),
29
re.compile('^building \[.*\]: targets for 0 source files that are out of date'),
30
re.compile('^loading pickled environment... done'),
31
re.compile('^loading cross citations... done \([0-9]* citations\).')
32
)
33
34
# replacements: pairs of regular expressions and their replacements,
35
# to be applied to Sphinx output.
36
37
replacements = ()
38
39
if 'inventory' in sys.argv:
40
# When building the inventory, ignore warnings about missing
41
# citations and the search index.
42
useless_chatter += (
43
re.compile('^None:[0-9]*: WARNING: citation not found: '),
44
re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.')
45
)
46
replacements += ([re.compile('build succeeded, [0-9]+ warning[s]?.'),
47
'build succeeded.'], )
48
49
# warnings: regular expressions (or strings) indicating a problem with
50
# docbuilding. Raise an exception if any of these occur.
51
52
warnings = (re.compile('Segmentation fault'),
53
re.compile('SEVERE'),
54
re.compile('ERROR'),
55
re.compile('^make.*Error'),
56
re.compile('Exception occurred'),
57
re.compile('Sphinx error'))
58
59
if 'pdf' not in sys.argv:
60
warnings += (re.compile('WARNING'),)
61
62
class SageSphinxLogger(object):
63
"""
64
This implements the file object interface to serve as sys.stdout
65
replacement.
66
"""
67
ansi_color = re.compile(r'\x1b\[[0-9;]*m')
68
ansi_reset = re.compile(r'\x1b\[39;49;00m')
69
prefix_len = 9
70
71
def __init__(self, stream, prefix):
72
self._stream = stream
73
self._color = stream.isatty()
74
prefix = prefix[0:self.prefix_len]
75
prefix = ('[{0:'+str(self.prefix_len)+'}]').format(prefix)
76
color = { 1:'darkgreen', 2:'red' }
77
color = color.get(stream.fileno(), 'lightgray')
78
self._prefix = sphinx.util.console.colorize(color, prefix)
79
80
81
def _filter_out(self, line):
82
line = re.sub(self.ansi_color, '', line)
83
global useless_chatter
84
for regex in useless_chatter:
85
if regex.match(line) is not None:
86
return True
87
return False
88
89
def _warnings(self, line):
90
global warnings
91
for regex in warnings:
92
if regex.search(line) is not None:
93
return True
94
return False
95
96
def _log_line(self, line):
97
if self._filter_out(line):
98
return
99
global replacements
100
for (old, new) in replacements:
101
line = old.sub(new, line)
102
line = self._prefix + ' ' + line.strip() + '\n'
103
if not self._color:
104
line = self.ansi_color.sub('', line)
105
self._stream.write(line)
106
self._stream.flush()
107
if self._warnings(line):
108
raise OSError(line)
109
110
_line_buffer = ''
111
112
def _break_long_lines(self):
113
"""
114
Break text that has been formated with string.ljust() back
115
into individual lines. Return partial output. Do nothing if
116
the filter rule matches, otherwise subsequent lines would be
117
not filtered out.
118
"""
119
if self._filter_out(self._line_buffer):
120
return
121
cols = sphinx.util.console._tw
122
lines = []
123
buf = self._line_buffer
124
while len(buf) > cols:
125
lines.append(buf[0:cols])
126
buf = buf[cols:]
127
lines.append(buf)
128
self._line_buffer = '\n'.join(lines)
129
130
def _write(self, string):
131
self._line_buffer += string
132
#self._break_long_lines()
133
lines = self._line_buffer.splitlines()
134
for i, line in enumerate(lines):
135
last = (i == len(lines)-1)
136
if last and not self._line_buffer.endswith('\n'):
137
self._line_buffer = line
138
return
139
self._log_line(line)
140
self._line_buffer = ''
141
142
143
# file object interface follows
144
145
closed = False
146
encoding = None
147
mode = 'w'
148
name = '<log>'
149
newlines = None
150
softspace = 0
151
152
def isatty(self):
153
return True
154
155
def close(self):
156
if self._line_buffer != '':
157
self._log_line(self._line_buffer)
158
self._line_buffer = ''
159
160
def flush(self):
161
self._stream.flush()
162
163
def write(self, str):
164
try:
165
self._write(str)
166
except OSError:
167
raise
168
except StandardError:
169
import traceback
170
traceback.print_exc(file=self._stream)
171
172
def writelines(self, sequence):
173
for line in sequence:
174
self.write(line)
175
176
177
output_dir = sys.argv[-1]
178
179
saved_stdout = sys.stdout
180
saved_stderr = sys.stderr
181
182
try:
183
sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir))
184
sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir))
185
sphinx.cmdline.main(sys.argv)
186
finally:
187
sys.stdout = saved_stdout
188
sys.stderr = saved_stderr
189
190