diff --git a/CHANGES b/CHANGES index fa1d9fd83..e87bd806a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,11 @@ Release 1.2.4 (in development) ============================== +Features added +-------------- + +* Exception logs now contain the last 10 messages emitted by Sphinx. + Release 1.2.3 (released Sep 1, 2014) ==================================== diff --git a/sphinx/application.py b/sphinx/application.py index 4fb9eb384..8a1c65630 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -17,17 +17,18 @@ import types import posixpath from os import path from cStringIO import StringIO +from collections import deque from docutils import nodes from docutils.parsers.rst import convert_directive_function, \ - directives, roles + directives, roles import sphinx from sphinx import package_dir, locale from sphinx.roles import XRefRole from sphinx.config import Config from sphinx.errors import SphinxError, SphinxWarning, ExtensionError, \ - VersionRequirementError, ConfigError + VersionRequirementError, ConfigError from sphinx.domains import ObjType, BUILTIN_DOMAINS from sphinx.domains.std import GenericObject, Target, StandardDomain from sphinx.builders import BUILTIN_BUILDERS @@ -95,6 +96,9 @@ class Sphinx(object): self._events = events.copy() + # keep last few messages for traceback + self.messagelog = deque(maxlen=10) + # say hello to the world self.info(bold('Running Sphinx v%s' % sphinx.__version__)) @@ -241,6 +245,7 @@ class Sphinx(object): wfile.write('\n') if hasattr(wfile, 'flush'): wfile.flush() + self.messagelog.append(message) def warn(self, message, location=None, prefix='WARNING: '): """Emit a warning. diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 44c76fafb..c02ecb53f 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -277,8 +277,8 @@ class Builder(object): # finish (write static files etc.) self.finish() - status = (self.app.statuscode == 0 and 'succeeded' - or 'finished with problems') + status = (self.app.statuscode == 0 + and 'succeeded' or 'finished with problems') if self.app._warncount: self.info(bold('build %s, %s warning%s.' % (status, self.app._warncount, @@ -387,7 +387,7 @@ class Builder(object): threads.append(t) # make sure all threads have finished - self.info(bold('waiting for workers... '))#, nonl=True) + self.info(bold('waiting for workers... ')) for t in threads: t.join() diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index cf3ae3279..91b7111ca 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -12,7 +12,6 @@ import os import re import sys -import shutil import fnmatch import tempfile import posixpath @@ -30,6 +29,7 @@ import jinja2 import sphinx from sphinx.errors import PycodeError from sphinx.util.pycompat import bytes +from sphinx.util.console import strip_colors # import other utilities; partly for backwards compatibility, so don't # prune unused ones indiscriminately @@ -176,6 +176,8 @@ _DEBUG_HEADER = '''\ # Python version: %s # Docutils version: %s %s # Jinja2 version: %s +# Last messages: +%s # Loaded extensions: ''' @@ -184,11 +186,17 @@ def save_traceback(app): import platform exc = traceback.format_exc() fd, path = tempfile.mkstemp('.log', 'sphinx-err-') + last_msgs = '' + if app is not None: + last_msgs = '\n'.join( + '# %s' % strip_colors(force_decode(s, 'utf-8')).strip() + for s in app.messagelog) os.write(fd, (_DEBUG_HEADER % (sphinx.__version__, platform.python_version(), docutils.__version__, docutils.__version_details__, - jinja2.__version__)).encode('utf-8')) + jinja2.__version__, + last_msgs)).encode('utf-8')) if app is not None: for extname, extmod in app._extensions.iteritems(): os.write(fd, ('# %s from %s\n' % ( diff --git a/sphinx/util/console.py b/sphinx/util/console.py index c2330102d..24a22d754 100644 --- a/sphinx/util/console.py +++ b/sphinx/util/console.py @@ -63,6 +63,9 @@ def coloron(): def colorize(name, text): return codes.get(name, '') + text + codes.get('reset', '') +def strip_colors(s): + return re.compile('\x1b.*?m').sub('', s) + def create_color_func(name): def inner(text): return colorize(name, text)