diff --git a/CHANGES b/CHANGES index b2c07ad5a..2e8a97434 100644 --- a/CHANGES +++ b/CHANGES @@ -31,6 +31,13 @@ New features added Release 0.5.1 (in development) ============================== +* Don't crash on failing doctests with non-ASCII characters. + +* Don't crash on writing status messages and warnings containing + unencodable characters. + +* Warn if a doctest extension block doesn't contain any code. + * Fix the handling of ``:param:`` and ``:type:`` doc fields when they contain markup (especially cross-referencing roles). diff --git a/sphinx/application.py b/sphinx/application.py index f7c57592a..91a724dc8 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -149,13 +149,20 @@ class Sphinx(object): def warn(self, message): self._warncount += 1 - self._warning.write('WARNING: %s\n' % message) + try: + self._warning.write('WARNING: %s\n' % message) + except UnicodeEncodeError: + encoding = getattr(self._warning, 'encoding', 'ascii') + self._warning.write(('WARNING: %s\n' % message).encode(encoding, 'replace')) def info(self, message='', nonl=False): - if nonl: + try: self._status.write(message) - else: - self._status.write(message + '\n') + except UnicodeEncodeError: + encoding = getattr(self._status, 'encoding', 'ascii') + self._status.write(message.encode(encoding, 'replace')) + if not nonl: + self._status.write('\n') self._status.flush() # general extensibility interface diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index b03f42fb3..8212028dd 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -13,6 +13,7 @@ import re import sys import time +import codecs import StringIO from os import path # circumvent relative import @@ -172,7 +173,8 @@ class DocTestBuilder(Builder): date = time.strftime('%Y-%m-%d %H:%M:%S') - self.outfile = file(path.join(self.outdir, 'output.txt'), 'w') + self.outfile = codecs.open(path.join(self.outdir, 'output.txt'), + 'w', encoding='utf-8') self.outfile.write('''\ Results of doctest builder run on %s ==================================%s @@ -232,8 +234,12 @@ Doctest summary return isinstance(node, (nodes.literal_block, nodes.comment)) \ and node.has_key('testnodetype') for node in doctree.traverse(condition): - code = TestCode(node.has_key('test') and node['test'] or node.astext(), - type=node.get('testnodetype', 'doctest'), + source = node.has_key('test') and node['test'] or node.astext() + if not source: + self.warn('no code/output in %s block at %s:%s' % + (node.get('testnodetype', 'doctest'), + self.env.doc2path(docname), node.line)) + code = TestCode(source, type=node.get('testnodetype', 'doctest'), lineno=node.line, options=node.get('options')) node_groups = node.get('groups', ['default']) if '*' in node_groups: