Drop support for Python 2.3.
[docutils.git] / test / DocutilsTestSupport.py
blobedece3faca5cf42f522d547fcdbf5d9afcfa13ee
1 # $Id$
2 # Authors: David Goodger <goodger@python.org>;
3 # Garth Kidd <garth@deadlybloodyserious.com>
4 # Copyright: This module has been placed in the public domain.
6 """
7 Exports the following:
9 :Modules:
10 - `statemachine` is 'docutils.statemachine'
11 - `nodes` is 'docutils.nodes'
12 - `urischemes` is 'docutils.urischemes'
13 - `utils` is 'docutils.utils'
14 - `transforms` is 'docutils.transforms'
15 - `states` is 'docutils.parsers.rst.states'
16 - `tableparser` is 'docutils.parsers.rst.tableparser'
18 :Classes:
19 - `StandardTestCase`
20 - `CustomTestCase`
21 - `CustomTestSuite`
22 - `TransformTestCase`
23 - `TransformTestSuite`
24 - `ParserTestCase`
25 - `ParserTestSuite`
26 - `ParserTransformTestCase`
27 - `PEPParserTestCase`
28 - `PEPParserTestSuite`
29 - `GridTableParserTestCase`
30 - `GridTableParserTestSuite`
31 - `SimpleTableParserTestCase`
32 - `SimpleTableParserTestSuite`
33 - `WriterPublishTestCase`
34 - `LatexWriterPublishTestCase`
35 - `PseudoXMLWriterPublishTestCase`
36 - `HtmlWriterPublishTestCase`
37 - `PublishTestSuite`
38 - `HtmlFragmentTestSuite`
39 - `DevNull` (output sink)
40 """
41 __docformat__ = 'reStructuredText'
43 import sys
44 import os
45 import unittest
46 import re
47 import inspect
48 import traceback
49 from pprint import pformat
51 testroot = os.path.abspath(os.path.dirname(__file__) or os.curdir)
52 os.chdir(testroot)
53 if sys.version_info >= (3,0):
54 sys.path.insert(0, os.path.normpath(os.path.join(testroot,
55 '..', 'build', 'lib')))
56 sys.path.append(os.path.normpath(os.path.join(testroot, '..',
57 'build', 'lib', 'extras')))
58 else:
59 sys.path.insert(0, os.path.normpath(os.path.join(testroot, '..')))
60 sys.path.append(os.path.normpath(os.path.join(testroot, '..', 'extras')))
61 sys.path.insert(0, testroot)
63 try:
64 import difflib
65 import package_unittest
66 import docutils
67 import docutils.core
68 from docutils import frontend, nodes, statemachine, urischemes, utils
69 from docutils.transforms import universal
70 from docutils.parsers import rst
71 from docutils.parsers.rst import states, tableparser, roles, languages
72 from docutils.readers import standalone, pep
73 from docutils.statemachine import StringList, string2lines
74 from docutils._compat import bytes
75 except ImportError:
76 # The importing module (usually __init__.py in one of the
77 # subdirectories) may catch ImportErrors in order to detect the
78 # absence of DocutilsTestSupport in sys.path. Thus, ImportErrors
79 # resulting from problems with importing Docutils modules must
80 # caught here.
81 traceback.print_exc()
82 sys.exit(1)
85 try:
86 import mypdb as pdb
87 except:
88 import pdb
91 # Hack to make repr(StringList) look like repr(list):
92 StringList.__repr__ = StringList.__str__
95 class DevNull:
97 """Output sink."""
99 def write(self, string):
100 pass
102 def close(self):
103 pass
106 class StandardTestCase(unittest.TestCase):
109 Helper class, providing the same interface as unittest.TestCase,
110 but with useful setUp and comparison methods.
113 def setUp(self):
114 os.chdir(testroot)
116 def assertEqual(self, first, second, msg=None):
117 """Fail if the two objects are unequal as determined by the '=='
118 operator.
120 if not first == second:
121 raise self.failureException, \
122 (msg or '%s != %s' % _format_str(first, second))
124 def assertNotEqual(self, first, second, msg=None):
125 """Fail if the two objects are equal as determined by the '=='
126 operator.
128 if first == second:
129 raise self.failureException, \
130 (msg or '%s == %s' % _format_str(first, second))
132 # aliases for assertion methods, deprecated since Python 2.7
134 failUnlessEqual = assertEquals = assertEqual
136 assertNotEquals = failIfEqual = assertNotEqual
139 class CustomTestCase(StandardTestCase):
142 Helper class, providing extended functionality over unittest.TestCase.
144 The methods assertEqual and assertNotEqual have been overwritten
145 to provide better support for multi-line strings. Furthermore,
146 see the compare_output method and the parameter list of __init__.
149 compare = difflib.Differ().compare
150 """Comparison method shared by all subclasses."""
152 def __init__(self, method_name, input, expected, id,
153 run_in_debugger=True, suite_settings=None):
155 Initialise the CustomTestCase.
157 Arguments:
159 method_name -- name of test method to run.
160 input -- input to the parser.
161 expected -- expected output from the parser.
162 id -- unique test identifier, used by the test framework.
163 run_in_debugger -- if true, run this test under the pdb debugger.
164 suite_settings -- settings overrides for this test suite.
166 self.id = id
167 self.input = input
168 self.expected = expected
169 self.run_in_debugger = run_in_debugger
170 self.suite_settings = suite_settings.copy() or {}
172 # Ring your mother.
173 unittest.TestCase.__init__(self, method_name)
175 def __str__(self):
177 Return string conversion. Overridden to give test id, in addition to
178 method name.
180 return '%s; %s' % (self.id, unittest.TestCase.__str__(self))
182 def __repr__(self):
183 return "<%s %s>" % (self.id, unittest.TestCase.__repr__(self))
185 def clear_roles(self):
186 # Language-specific roles and roles added by the
187 # "default-role" and "role" directives are currently stored
188 # globally in the roles._roles dictionary. This workaround
189 # empties that dictionary.
190 roles._roles = {}
192 def setUp(self):
193 StandardTestCase.setUp(self)
194 self.clear_roles()
196 def compare_output(self, input, output, expected):
197 """`input`, `output`, and `expected` should all be strings."""
198 if isinstance(input, unicode):
199 input = input.encode('raw_unicode_escape')
200 if sys.version_info > (3,):
201 # API difference: Python 3's node.__str__ doesn't escape
202 #assert expected is None or isinstance(expected, unicode)
203 if isinstance(expected, bytes):
204 expected = expected.decode('utf-8')
205 if isinstance(output, bytes):
206 output = output.decode('utf-8')
207 else:
208 if isinstance(expected, unicode):
209 expected = expected.encode('raw_unicode_escape')
210 if isinstance(output, unicode):
211 output = output.encode('raw_unicode_escape')
212 # Normalize line endings:
213 if expected:
214 expected = '\n'.join(expected.splitlines())
215 if output:
216 output = '\n'.join(output.splitlines())
217 try:
218 self.assertEqual(output, expected)
219 except AssertionError, error:
220 print >>sys.stderr, '\n%s\ninput:' % (self,)
221 print >>sys.stderr, input
222 try:
223 comparison = ''.join(self.compare(expected.splitlines(1),
224 output.splitlines(1)))
225 print >>sys.stderr, '-: expected\n+: output'
226 print >>sys.stderr, comparison
227 except AttributeError: # expected or output not a string
228 # alternative output for non-strings:
229 print >>sys.stderr, 'expected: %r' % expected
230 print >>sys.stderr, 'output: %r' % output
231 raise error
234 class CustomTestSuite(unittest.TestSuite):
237 A collection of CustomTestCases.
239 Provides test suite ID generation and a method for adding test cases.
242 id = ''
243 """Identifier for the TestSuite. Prepended to the
244 TestCase identifiers to make identification easier."""
246 next_test_case_id = 0
247 """The next identifier to use for non-identified test cases."""
249 def __init__(self, tests=(), id=None, suite_settings=None):
251 Initialize the CustomTestSuite.
253 Arguments:
255 id -- identifier for the suite, prepended to test cases.
256 suite_settings -- settings overrides for this test suite.
258 unittest.TestSuite.__init__(self, tests)
259 self.suite_settings = suite_settings or {}
260 if id is None:
261 mypath = os.path.abspath(
262 sys.modules[CustomTestSuite.__module__].__file__)
263 outerframes = inspect.getouterframes(inspect.currentframe())
264 for outerframe in outerframes[1:]:
265 if outerframe[3] != '__init__':
266 callerpath = outerframe[1]
267 if callerpath is None:
268 # It happens sometimes. Why is a mystery.
269 callerpath = os.getcwd()
270 callerpath = os.path.abspath(callerpath)
271 break
272 mydir, myname = os.path.split(mypath)
273 if not mydir:
274 mydir = os.curdir
275 if callerpath.startswith(mydir):
276 self.id = callerpath[len(mydir) + 1:] # caller's module
277 else:
278 self.id = callerpath
279 else:
280 self.id = id
282 def addTestCase(self, test_case_class, method_name, input, expected,
283 id=None, run_in_debugger=False, **kwargs):
285 Create a CustomTestCase in the CustomTestSuite.
286 Also return it, just in case.
288 Arguments:
290 test_case_class -- the CustomTestCase to add
291 method_name -- a string; CustomTestCase.method_name is the test
292 input -- input to the parser.
293 expected -- expected output from the parser.
294 id -- unique test identifier, used by the test framework.
295 run_in_debugger -- if true, run this test under the pdb debugger.
297 if id is None: # generate id if required
298 id = self.next_test_case_id
299 self.next_test_case_id += 1
300 # test identifier will become suiteid.testid
301 tcid = '%s: %s' % (self.id, id)
302 # suite_settings may be passed as a parameter;
303 # if not, set from attribute:
304 kwargs.setdefault('suite_settings', self.suite_settings)
305 # generate and add test case
306 tc = test_case_class(method_name, input, expected, tcid,
307 run_in_debugger=run_in_debugger, **kwargs)
308 self.addTest(tc)
309 return tc
311 def generate_no_tests(self, *args, **kwargs):
312 pass
315 class TransformTestCase(CustomTestCase):
318 Output checker for the transform.
320 Should probably be called TransformOutputChecker, but I can deal with
321 that later when/if someone comes up with a category of transform test
322 cases that have nothing to do with the input and output of the transform.
325 option_parser = frontend.OptionParser(components=(rst.Parser,))
326 settings = option_parser.get_default_values()
327 settings.report_level = 1
328 settings.halt_level = 5
329 settings.debug = package_unittest.debug
330 settings.warning_stream = DevNull()
331 unknown_reference_resolvers = ()
333 def __init__(self, *args, **kwargs):
334 self.transforms = kwargs['transforms']
335 """List of transforms to perform for this test case."""
337 self.parser = kwargs['parser']
338 """Input parser for this test case."""
340 del kwargs['transforms'], kwargs['parser'] # only wanted here
341 CustomTestCase.__init__(self, *args, **kwargs)
343 def supports(self, format):
344 return 1
346 def test_transforms(self):
347 if self.run_in_debugger:
348 pdb.set_trace()
349 settings = self.settings.copy()
350 settings.__dict__.update(self.suite_settings)
351 document = utils.new_document('test data', settings)
352 self.parser.parse(self.input, document)
353 # Don't do a ``populate_from_components()`` because that would
354 # enable the Transformer's default transforms.
355 document.transformer.add_transforms(self.transforms)
356 document.transformer.add_transform(universal.TestMessages)
357 document.transformer.components['writer'] = self
358 document.transformer.apply_transforms()
359 output = document.pformat()
360 self.compare_output(self.input, output, self.expected)
362 def test_transforms_verbosely(self):
363 if self.run_in_debugger:
364 pdb.set_trace()
365 print '\n', self.id
366 print '-' * 70
367 print self.input
368 settings = self.settings.copy()
369 settings.__dict__.update(self.suite_settings)
370 document = utils.new_document('test data', settings)
371 self.parser.parse(self.input, document)
372 print '-' * 70
373 print document.pformat()
374 for transformClass in self.transforms:
375 transformClass(document).apply()
376 output = document.pformat()
377 print '-' * 70
378 print output
379 self.compare_output(self.input, output, self.expected)
382 class TransformTestSuite(CustomTestSuite):
385 A collection of TransformTestCases.
387 A TransformTestSuite instance manufactures TransformTestCases,
388 keeps track of them, and provides a shared test fixture (a-la
389 setUp and tearDown).
392 def __init__(self, parser, suite_settings=None):
393 self.parser = parser
394 """Parser shared by all test cases."""
396 CustomTestSuite.__init__(self, suite_settings=suite_settings)
398 def generateTests(self, dict, dictname='totest',
399 testmethod='test_transforms'):
401 Stock the suite with test cases generated from a test data dictionary.
403 Each dictionary key (test type's name) maps to a tuple, whose
404 first item is a list of transform classes and whose second
405 item is a list of tests. Each test is a list: input, expected
406 output, optional modifier. The optional third entry, a
407 behavior modifier, can be 0 (temporarily disable this test) or
408 1 (run this test under the pdb debugger). Tests should be
409 self-documenting and not require external comments.
411 for name, (transforms, cases) in dict.items():
412 for casenum in range(len(cases)):
413 case = cases[casenum]
414 run_in_debugger = False
415 if len(case)==3:
416 # TODO: (maybe) change the 3rd argument to a dict, so it
417 # can handle more cases by keyword ('disable', 'debug',
418 # 'settings'), here and in other generateTests methods.
419 # But there's also the method that
420 # HtmlPublishPartsTestSuite uses <DJG>
421 if case[2]:
422 run_in_debugger = True
423 else:
424 continue
425 self.addTestCase(
426 TransformTestCase, testmethod,
427 transforms=transforms, parser=self.parser,
428 input=case[0], expected=case[1],
429 id='%s[%r][%s]' % (dictname, name, casenum),
430 run_in_debugger=run_in_debugger)
433 class ParserTestCase(CustomTestCase):
436 Output checker for the parser.
438 Should probably be called ParserOutputChecker, but I can deal with
439 that later when/if someone comes up with a category of parser test
440 cases that have nothing to do with the input and output of the parser.
443 parser = rst.Parser()
444 """Parser shared by all ParserTestCases."""
446 option_parser = frontend.OptionParser(components=(rst.Parser,))
447 settings = option_parser.get_default_values()
448 settings.report_level = 5
449 settings.halt_level = 5
450 settings.debug = package_unittest.debug
452 def test_parser(self):
453 if self.run_in_debugger:
454 pdb.set_trace()
455 settings = self.settings.copy()
456 settings.__dict__.update(self.suite_settings)
457 document = utils.new_document('test data', settings)
458 self.parser.parse(self.input, document)
459 output = document.pformat()
460 self.compare_output(self.input, output, self.expected)
463 class ParserTestSuite(CustomTestSuite):
466 A collection of ParserTestCases.
468 A ParserTestSuite instance manufactures ParserTestCases,
469 keeps track of them, and provides a shared test fixture (a-la
470 setUp and tearDown).
473 test_case_class = ParserTestCase
475 def generateTests(self, dict, dictname='totest'):
477 Stock the suite with test cases generated from a test data dictionary.
479 Each dictionary key (test type name) maps to a list of tests. Each
480 test is a list: input, expected output, optional modifier. The
481 optional third entry, a behavior modifier, can be 0 (temporarily
482 disable this test) or 1 (run this test under the pdb debugger). Tests
483 should be self-documenting and not require external comments.
485 for name, cases in dict.items():
486 for casenum in range(len(cases)):
487 case = cases[casenum]
488 run_in_debugger = False
489 if len(case)==3:
490 if case[2]:
491 run_in_debugger = True
492 else:
493 continue
494 self.addTestCase(
495 self.test_case_class, 'test_parser',
496 input=case[0], expected=case[1],
497 id='%s[%r][%s]' % (dictname, name, casenum),
498 run_in_debugger=run_in_debugger)
501 class PEPParserTestCase(ParserTestCase):
503 """PEP-specific parser test case."""
505 parser = rst.Parser(rfc2822=True, inliner=rst.states.Inliner())
506 """Parser shared by all PEPParserTestCases."""
508 option_parser = frontend.OptionParser(components=(rst.Parser, pep.Reader))
509 settings = option_parser.get_default_values()
510 settings.report_level = 5
511 settings.halt_level = 5
512 settings.debug = package_unittest.debug
515 class PEPParserTestSuite(ParserTestSuite):
517 """A collection of PEPParserTestCases."""
519 test_case_class = PEPParserTestCase
522 class GridTableParserTestCase(CustomTestCase):
524 parser = tableparser.GridTableParser()
526 def test_parse_table(self):
527 self.parser.setup(StringList(string2lines(self.input), 'test data'))
528 try:
529 self.parser.find_head_body_sep()
530 self.parser.parse_table()
531 output = self.parser.cells
532 except Exception, details:
533 output = '%s: %s' % (details.__class__.__name__, details)
534 self.compare_output(self.input, pformat(output) + '\n',
535 pformat(self.expected) + '\n')
537 def test_parse(self):
538 try:
539 output = self.parser.parse(StringList(string2lines(self.input),
540 'test data'))
541 except Exception, details:
542 output = '%s: %s' % (details.__class__.__name__, details)
543 self.compare_output(self.input, pformat(output) + '\n',
544 pformat(self.expected) + '\n')
547 class GridTableParserTestSuite(CustomTestSuite):
550 A collection of GridTableParserTestCases.
552 A GridTableParserTestSuite instance manufactures GridTableParserTestCases,
553 keeps track of them, and provides a shared test fixture (a-la setUp and
554 tearDown).
557 test_case_class = GridTableParserTestCase
559 def generateTests(self, dict, dictname='totest'):
561 Stock the suite with test cases generated from a test data dictionary.
563 Each dictionary key (test type name) maps to a list of tests. Each
564 test is a list: an input table, expected output from parse_table(),
565 expected output from parse(), optional modifier. The optional fourth
566 entry, a behavior modifier, can be 0 (temporarily disable this test)
567 or 1 (run this test under the pdb debugger). Tests should be
568 self-documenting and not require external comments.
570 for name, cases in dict.items():
571 for casenum in range(len(cases)):
572 case = cases[casenum]
573 run_in_debugger = False
574 if len(case) == 4:
575 if case[-1]:
576 run_in_debugger = True
577 else:
578 continue
579 self.addTestCase(self.test_case_class, 'test_parse_table',
580 input=case[0], expected=case[1],
581 id='%s[%r][%s]' % (dictname, name, casenum),
582 run_in_debugger=run_in_debugger)
583 self.addTestCase(self.test_case_class, 'test_parse',
584 input=case[0], expected=case[2],
585 id='%s[%r][%s]' % (dictname, name, casenum),
586 run_in_debugger=run_in_debugger)
589 class SimpleTableParserTestCase(GridTableParserTestCase):
591 parser = tableparser.SimpleTableParser()
594 class SimpleTableParserTestSuite(CustomTestSuite):
597 A collection of SimpleTableParserTestCases.
600 test_case_class = SimpleTableParserTestCase
602 def generateTests(self, dict, dictname='totest'):
604 Stock the suite with test cases generated from a test data dictionary.
606 Each dictionary key (test type name) maps to a list of tests. Each
607 test is a list: an input table, expected output from parse(), optional
608 modifier. The optional third entry, a behavior modifier, can be 0
609 (temporarily disable this test) or 1 (run this test under the pdb
610 debugger). Tests should be self-documenting and not require external
611 comments.
613 for name, cases in dict.items():
614 for casenum in range(len(cases)):
615 case = cases[casenum]
616 run_in_debugger = False
617 if len(case) == 3:
618 if case[-1]:
619 run_in_debugger = True
620 else:
621 continue
622 self.addTestCase(self.test_case_class, 'test_parse',
623 input=case[0], expected=case[1],
624 id='%s[%r][%s]' % (dictname, name, casenum),
625 run_in_debugger=run_in_debugger)
628 class PythonModuleParserTestCase(CustomTestCase):
630 def test_parser(self):
631 if self.run_in_debugger:
632 pdb.set_trace()
633 try:
634 import compiler
635 except ImportError:
636 # skip on Python 3
637 return
638 from docutils.readers.python import moduleparser
639 module = moduleparser.parse_module(self.input, 'test data').pformat()
640 output = str(module)
641 self.compare_output(self.input, output, self.expected)
643 def test_token_parser_rhs(self):
644 if self.run_in_debugger:
645 pdb.set_trace()
646 try:
647 import compiler
648 except ImportError:
649 # skip on Python 3
650 return
651 from docutils.readers.python import moduleparser
652 tr = moduleparser.TokenParser(self.input)
653 output = tr.rhs(1)
654 self.compare_output(self.input, output, self.expected)
657 class PythonModuleParserTestSuite(CustomTestSuite):
660 A collection of PythonModuleParserTestCase.
663 def generateTests(self, dict, dictname='totest',
664 testmethod='test_parser'):
666 Stock the suite with test cases generated from a test data dictionary.
668 Each dictionary key (test type's name) maps to a list of tests. Each
669 test is a list: input, expected output, optional modifier. The
670 optional third entry, a behavior modifier, can be 0 (temporarily
671 disable this test) or 1 (run this test under the pdb debugger). Tests
672 should be self-documenting and not require external comments.
674 for name, cases in dict.items():
675 for casenum in range(len(cases)):
676 case = cases[casenum]
677 run_in_debugger = False
678 if len(case)==3:
679 if case[2]:
680 run_in_debugger = True
681 else:
682 continue
683 self.addTestCase(
684 PythonModuleParserTestCase, testmethod,
685 input=case[0], expected=case[1],
686 id='%s[%r][%s]' % (dictname, name, casenum),
687 run_in_debugger=run_in_debugger)
690 class WriterPublishTestCase(CustomTestCase, docutils.SettingsSpec):
693 Test case for publish.
696 settings_default_overrides = {'_disable_config': 1,
697 'strict_visitor': 1}
698 writer_name = '' # set in subclasses or constructor
700 def __init__(self, *args, **kwargs):
701 if 'writer_name' in kwargs:
702 self.writer_name = kwargs['writer_name']
703 del kwargs['writer_name']
704 CustomTestCase.__init__(self, *args, **kwargs)
706 def test_publish(self):
707 if self.run_in_debugger:
708 pdb.set_trace()
709 output = docutils.core.publish_string(
710 source=self.input,
711 reader_name='standalone',
712 parser_name='restructuredtext',
713 writer_name=self.writer_name,
714 settings_spec=self,
715 settings_overrides=self.suite_settings)
716 self.compare_output(self.input, output, self.expected)
719 class PublishTestSuite(CustomTestSuite):
721 def __init__(self, writer_name, suite_settings=None):
723 `writer_name` is the name of the writer to use.
725 CustomTestSuite.__init__(self, suite_settings=suite_settings)
726 self.test_class = WriterPublishTestCase
727 self.writer_name = writer_name
729 def generateTests(self, dict, dictname='totest'):
730 for name, cases in dict.items():
731 for casenum in range(len(cases)):
732 case = cases[casenum]
733 run_in_debugger = False
734 if len(case)==3:
735 if case[2]:
736 run_in_debugger = True
737 else:
738 continue
739 self.addTestCase(
740 self.test_class, 'test_publish',
741 input=case[0], expected=case[1],
742 id='%s[%r][%s]' % (dictname, name, casenum),
743 run_in_debugger=run_in_debugger,
744 # Passed to constructor of self.test_class:
745 writer_name=self.writer_name)
748 class HtmlPublishPartsTestSuite(CustomTestSuite):
750 def generateTests(self, dict, dictname='totest'):
751 for name, (settings_overrides, cases) in dict.items():
752 settings = self.suite_settings.copy()
753 settings.update(settings_overrides)
754 for casenum in range(len(cases)):
755 case = cases[casenum]
756 run_in_debugger = False
757 if len(case)==3:
758 if case[2]:
759 run_in_debugger = True
760 else:
761 continue
762 self.addTestCase(
763 HtmlWriterPublishPartsTestCase, 'test_publish',
764 input=case[0], expected=case[1],
765 id='%s[%r][%s]' % (dictname, name, casenum),
766 run_in_debugger=run_in_debugger,
767 suite_settings=settings)
770 class HtmlWriterPublishPartsTestCase(WriterPublishTestCase):
773 Test case for HTML writer via the publish_parts interface.
776 writer_name = 'html'
778 settings_default_overrides = \
779 WriterPublishTestCase.settings_default_overrides.copy()
780 settings_default_overrides['stylesheet'] = ''
782 def test_publish(self):
783 if self.run_in_debugger:
784 pdb.set_trace()
785 parts = docutils.core.publish_parts(
786 source=self.input,
787 reader_name='standalone',
788 parser_name='restructuredtext',
789 writer_name=self.writer_name,
790 settings_spec=self,
791 settings_overrides=self.suite_settings)
792 output = self.format_output(parts)
793 # interpolate standard variables:
794 expected = self.expected % {'version': docutils.__version__}
795 self.compare_output(self.input, output, expected)
797 standard_content_type_template = ('<meta http-equiv="Content-Type"'
798 ' content="text/html; charset=%s" />\n')
799 standard_generator_template = (
800 '<meta name="generator"'
801 ' content="Docutils %s: http://docutils.sourceforge.net/" />\n')
802 standard_html_meta_value = (
803 standard_content_type_template
804 + standard_generator_template % docutils.__version__)
805 standard_meta_value = standard_html_meta_value % 'utf-8'
806 standard_html_prolog = """\
807 <?xml version="1.0" encoding="%s" ?>
808 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
811 def format_output(self, parts):
812 """Minimize & standardize the output."""
813 # remove redundant parts & uninteresting parts:
814 del parts['whole']
815 assert parts['body'] == parts['fragment']
816 del parts['body']
817 del parts['body_pre_docinfo']
818 del parts['body_prefix']
819 del parts['body_suffix']
820 del parts['head']
821 del parts['head_prefix']
822 del parts['encoding']
823 del parts['version']
824 # remove standard portions:
825 parts['meta'] = parts['meta'].replace(self.standard_meta_value, '')
826 parts['html_head'] = parts['html_head'].replace(
827 self.standard_html_meta_value, '...')
828 parts['html_prolog'] = parts['html_prolog'].replace(
829 self.standard_html_prolog, '')
830 # remove empty values:
831 for key in parts.keys():
832 if not parts[key]:
833 del parts[key]
834 # standard output format:
835 keys = parts.keys()
836 keys.sort()
837 output = []
838 for key in keys:
839 output.append("%r: '''%s'''"
840 % (key, parts[key]))
841 if output[-1].endswith("\n'''"):
842 output[-1] = output[-1][:-4] + "\\n'''"
843 return '{' + ',\n '.join(output) + '}\n'
846 def exception_data(func, *args, **kwds):
848 Execute `func(*args, **kwds)` and return the resulting exception, the
849 exception arguments, and the formatted exception string.
851 try:
852 func(*args, **kwds)
853 except Exception, detail:
854 return (detail, detail.args,
855 '%s: %s' % (detail.__class__.__name__, detail))
858 def _format_str(*args):
859 r"""
860 Return a tuple containing representations of all args.
862 Same as map(repr, args) except that it returns multi-line
863 representations for strings containing newlines, e.g.::
865 '''\
866 foo \n\
869 baz'''
871 instead of::
873 'foo \nbar\n\nbaz'
875 This is a helper function for CustomTestCase.
877 return_tuple = []
878 for i in args:
879 r = repr(i)
880 if ( (isinstance(i, bytes) or isinstance(i, unicode))
881 and '\n' in i):
882 stripped = ''
883 if isinstance(i, unicode) and r.startswith('u'):
884 stripped = r[0]
885 r = r[1:]
886 elif isinstance(i, bytes) and r.startswith('b'):
887 stripped = r[0]
888 r = r[1:]
889 # quote_char = "'" or '"'
890 quote_char = r[0]
891 assert quote_char in ("'", '"'), quote_char
892 assert r[0] == r[-1]
893 r = r[1:-1]
894 r = (stripped + 3 * quote_char + '\\\n' +
895 re.sub(r'(?<!\\)((\\\\)*)\\n', r'\1\n', r) +
896 3 * quote_char)
897 r = re.sub(r' \n', r' \\n\\\n', r)
898 return_tuple.append(r)
899 return tuple(return_tuple)