Path: blob/master/src/doc/common/custom-sphinx-build.py
8815 views
"""1This is Sage's version of the sphinx-build script23Enhancements are:45* import the Sage library to access the docstrings, otherwise doc6buliding doesn't work.78* redirect stdout to our own logger, and remove some unwanted chatter.9"""1011# override the fancy multi-line formatting12def term_width_line(text):13return text + '\n'1415sphinx.util.console.term_width_line = term_width_line161718# useless_chatter: regular expressions to be filtered from Sphinx19# output.2021useless_chatter = (22re.compile('^$'),23re.compile('^Running Sphinx v'),24re.compile('^loading intersphinx inventory from '),25re.compile('^Compiling a sub-document'),26re.compile('^updating environment: 0 added, 0 changed, 0 removed'),27re.compile('^looking for now-outdated files... none found'),28re.compile('^building \[.*\]: targets for 0 source files that are out of date'),29re.compile('^loading pickled environment... done'),30re.compile('^loading cross citations... done \([0-9]* citations\).')31)3233# replacements: pairs of regular expressions and their replacements,34# to be applied to Sphinx output.3536replacements = ()3738if 'inventory' in sys.argv:39# When building the inventory, ignore warnings about missing40# citations and the search index.41useless_chatter += (42re.compile('^None:[0-9]*: WARNING: citation not found: '),43re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.')44)45replacements += ([re.compile('build succeeded, [0-9]+ warning[s]?.'),46'build succeeded.'], )4748# warnings: regular expressions (or strings) indicating a problem with49# docbuilding. Raise an exception if any of these occur.5051warnings = (re.compile('Segmentation fault'),52re.compile('SEVERE'),53re.compile('ERROR'),54re.compile('^make.*Error'),55re.compile('Exception occurred'),56re.compile('Sphinx error'))5758if 'pdf' not in sys.argv:59warnings += (re.compile('WARNING'),)6061class SageSphinxLogger(object):62"""63This implements the file object interface to serve as sys.stdout64replacement.65"""66ansi_color = re.compile(r'\x1b\[[0-9;]*m')67ansi_reset = re.compile(r'\x1b\[39;49;00m')68prefix_len = 96970def __init__(self, stream, prefix):71self._stream = stream72self._color = stream.isatty()73prefix = prefix[0:self.prefix_len]74prefix = ('[{0:'+str(self.prefix_len)+'}]').format(prefix)75color = { 1:'darkgreen', 2:'red' }76color = color.get(stream.fileno(), 'lightgray')77self._prefix = sphinx.util.console.colorize(color, prefix)787980def _filter_out(self, line):81line = re.sub(self.ansi_color, '', line)82global useless_chatter83for regex in useless_chatter:84if regex.match(line) is not None:85return True86return False8788def _warnings(self, line):89global warnings90for regex in warnings:91if regex.search(line) is not None:92return True93return False9495def _log_line(self, line):96if self._filter_out(line):97return98global replacements99for (old, new) in replacements:100line = old.sub(new, line)101line = self._prefix + ' ' + line.strip() + '\n'102if not self._color:103line = self.ansi_color.sub('', line)104self._stream.write(line)105self._stream.flush()106if self._warnings(line):107raise OSError(line)108109_line_buffer = ''110111def _break_long_lines(self):112"""113Break text that has been formated with string.ljust() back114into individual lines. Return partial output. Do nothing if115the filter rule matches, otherwise subsequent lines would be116not filtered out.117"""118if self._filter_out(self._line_buffer):119return120cols = sphinx.util.console._tw121lines = []122buf = self._line_buffer123while len(buf) > cols:124lines.append(buf[0:cols])125buf = buf[cols:]126lines.append(buf)127self._line_buffer = '\n'.join(lines)128129def _write(self, string):130self._line_buffer += string131#self._break_long_lines()132lines = self._line_buffer.splitlines()133for i, line in enumerate(lines):134last = (i == len(lines)-1)135if last and not self._line_buffer.endswith('\n'):136self._line_buffer = line137return138self._log_line(line)139self._line_buffer = ''140141142# file object interface follows143144closed = False145encoding = None146mode = 'w'147name = '<log>'148newlines = None149softspace = 0150151def isatty(self):152return True153154def close(self):155if self._line_buffer != '':156self._log_line(self._line_buffer)157self._line_buffer = ''158159def flush(self):160self._stream.flush()161162def write(self, str):163try:164self._write(str)165except OSError:166raise167except StandardError:168import traceback169traceback.print_exc(file=self._stream)170171def writelines(self, sequence):172for line in sequence:173self.write(line)174175176output_dir = sys.argv[-1]177178saved_stdout = sys.stdout179saved_stderr = sys.stderr180181try:182sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir))183sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir))184sphinx.cmdline.main(sys.argv)185finally:186sys.stdout = saved_stdout187sys.stderr = saved_stderr188189190