support for new python.org and pep2pyramid.py
[docutils.git] / test / DocutilsTestSupport.py
blobc30b263d6d88ad02afacfe5bcaaf6dc0cd80608e
1 # Authors: David Goodger; Garth Kidd
2 # Contact: goodger@users.sourceforge.net
3 # Revision: $Revision$
4 # Date: $Date$
5 # Copyright: This module has been placed in the public domain.
7 """
8 Exports the following:
10 :Modules:
11 - `statemachine` is 'docutils.statemachine'
12 - `nodes` is 'docutils.nodes'
13 - `urischemes` is 'docutils.urischemes'
14 - `utils` is 'docutils.utils'
15 - `transforms` is 'docutils.transforms'
16 - `states` is 'docutils.parsers.rst.states'
17 - `tableparser` is 'docutils.parsers.rst.tableparser'
19 :Classes:
20 - `StandardTestCase`
21 - `CustomTestCase`
22 - `CustomTestSuite`
23 - `TransformTestCase`
24 - `TransformTestSuite`
25 - `ParserTestCase`
26 - `ParserTestSuite`
27 - `ParserTransformTestCase`
28 - `PEPParserTestCase`
29 - `PEPParserTestSuite`
30 - `GridTableParserTestCase`
31 - `GridTableParserTestSuite`
32 - `SimpleTableParserTestCase`
33 - `SimpleTableParserTestSuite`
34 - `WriterPublishTestCase`
35 - `LatexWriterPublishTestCase`
36 - `PseudoXMLWriterPublishTestCase`
37 - `HtmlWriterPublishTestCase`
38 - `PublishTestSuite`
39 - `HtmlFragmentTestSuite`
40 - `DevNull` (output sink)
41 """
42 __docformat__ = 'reStructuredText'
44 import sys
45 import os
47 testroot = os.path.abspath(os.path.dirname(__file__) or os.curdir)
48 os.chdir(testroot)
49 sys.path.insert(0, os.path.normpath(os.path.join(testroot, '..')))
50 sys.path.insert(0, testroot)
51 sys.path.append(os.path.normpath(os.path.join(testroot, '..', 'extras')))
53 import unittest
54 import docutils_difflib
55 import inspect
56 from pprint import pformat
57 from types import UnicodeType, StringType
58 import package_unittest
59 import docutils
60 import docutils.core
61 from docutils import frontend, nodes, statemachine, urischemes, utils
62 from docutils.transforms import universal
63 from docutils.parsers import rst
64 from docutils.parsers.rst import states, tableparser, roles, languages
65 from docutils.readers import standalone, pep
66 from docutils.statemachine import StringList, string2lines
68 try:
69 from docutils.readers.python import moduleparser
70 from tokenize import generate_tokens
71 del generate_tokens
72 except ImportError: # moduleparser depends on modules added in Python 2.2
73 moduleparser = None
75 try:
76 import mypdb as pdb
77 except:
78 import pdb
81 # Hack to make repr(StringList) look like repr(list):
82 StringList.__repr__ = StringList.__str__
85 class DevNull:
87 """Output sink."""
89 def write(self, string):
90 pass
92 def close(self):
93 pass
96 class StandardTestCase(unittest.TestCase):
98 """
99 Helper class, providing the same interface as unittest.TestCase,
100 but with useful setUp and comparison methods.
103 def setUp(self):
104 os.chdir(testroot)
106 def failUnlessEqual(self, first, second, msg=None):
107 """Fail if the two objects are unequal as determined by the '=='
108 operator.
110 if not first == second:
111 raise self.failureException, \
112 (msg or '%s != %s' % _format_str(first, second))
114 def failIfEqual(self, first, second, msg=None):
115 """Fail if the two objects are equal as determined by the '=='
116 operator.
118 if first == second:
119 raise self.failureException, \
120 (msg or '%s == %s' % _format_str(first, second))
122 # Synonyms for assertion methods
124 assertEqual = assertEquals = failUnlessEqual
126 assertNotEqual = assertNotEquals = failIfEqual
129 class CustomTestCase(StandardTestCase):
132 Helper class, providing extended functionality over unittest.TestCase.
134 The methods failUnlessEqual and failIfEqual have been overwritten
135 to provide better support for multi-line strings. Furthermore,
136 see the compare_output method and the parameter list of __init__.
139 compare = docutils_difflib.Differ().compare
140 """Comparison method shared by all subclasses."""
142 def __init__(self, method_name, input, expected, id, run_in_debugger=0,
143 suite_settings=None):
145 Initialise the CustomTestCase.
147 Arguments:
149 method_name -- name of test method to run.
150 input -- input to the parser.
151 expected -- expected output from the parser.
152 id -- unique test identifier, used by the test framework.
153 run_in_debugger -- if true, run this test under the pdb debugger.
154 suite_settings -- settings overrides for this test suite.
156 self.id = id
157 self.input = input
158 self.expected = expected
159 self.run_in_debugger = run_in_debugger
160 self.suite_settings = suite_settings or {}
162 # Ring your mother.
163 unittest.TestCase.__init__(self, method_name)
165 def __str__(self):
167 Return string conversion. Overridden to give test id, in addition to
168 method name.
170 return '%s; %s' % (self.id, unittest.TestCase.__str__(self))
172 def __repr__(self):
173 return "<%s %s>" % (self.id, unittest.TestCase.__repr__(self))
175 def clear_roles(self):
176 # Language-specific roles and roles added by the
177 # "default-role" and "role" directives are currently stored
178 # globally in the roles._roles dictionary. This workaround
179 # empties that dictionary.
180 roles._roles = {}
182 def setUp(self):
183 StandardTestCase.setUp(self)
184 self.clear_roles()
186 def compare_output(self, input, output, expected):
187 """`input`, `output`, and `expected` should all be strings."""
188 if isinstance(input, UnicodeType):
189 input = input.encode('raw_unicode_escape')
190 if isinstance(output, UnicodeType):
191 output = output.encode('raw_unicode_escape')
192 if isinstance(expected, UnicodeType):
193 expected = expected.encode('raw_unicode_escape')
194 try:
195 self.assertEquals(output, expected)
196 except AssertionError, error:
197 print >>sys.stderr, '\n%s\ninput:' % (self,)
198 print >>sys.stderr, input
199 try:
200 comparison = ''.join(self.compare(expected.splitlines(1),
201 output.splitlines(1)))
202 print >>sys.stderr, '-: expected\n+: output'
203 print >>sys.stderr, comparison
204 except AttributeError: # expected or output not a string
205 # alternative output for non-strings:
206 print >>sys.stderr, 'expected: %r' % expected
207 print >>sys.stderr, 'output: %r' % output
208 raise error
211 class CustomTestSuite(unittest.TestSuite):
214 A collection of CustomTestCases.
216 Provides test suite ID generation and a method for adding test cases.
219 id = ''
220 """Identifier for the TestSuite. Prepended to the
221 TestCase identifiers to make identification easier."""
223 next_test_case_id = 0
224 """The next identifier to use for non-identified test cases."""
226 def __init__(self, tests=(), id=None, suite_settings=None):
228 Initialize the CustomTestSuite.
230 Arguments:
232 id -- identifier for the suite, prepended to test cases.
233 suite_settings -- settings overrides for this test suite.
235 unittest.TestSuite.__init__(self, tests)
236 self.suite_settings = suite_settings or {}
237 if id is None:
238 mypath = os.path.abspath(
239 sys.modules[CustomTestSuite.__module__].__file__)
240 outerframes = inspect.getouterframes(inspect.currentframe())
241 for outerframe in outerframes[1:]:
242 if outerframe[3] != '__init__':
243 callerpath = outerframe[1]
244 if callerpath is None:
245 # It happens sometimes. Why is a mystery.
246 callerpath = os.getcwd()
247 callerpath = os.path.abspath(callerpath)
248 break
249 mydir, myname = os.path.split(mypath)
250 if not mydir:
251 mydir = os.curdir
252 if callerpath.startswith(mydir):
253 self.id = callerpath[len(mydir) + 1:] # caller's module
254 else:
255 self.id = callerpath
256 else:
257 self.id = id
259 def addTestCase(self, test_case_class, method_name, input, expected,
260 id=None, run_in_debugger=0, **kwargs):
262 Create a CustomTestCase in the CustomTestSuite.
263 Also return it, just in case.
265 Arguments:
267 test_case_class -- the CustomTestCase to add
268 method_name -- a string; CustomTestCase.method_name is the test
269 input -- input to the parser.
270 expected -- expected output from the parser.
271 id -- unique test identifier, used by the test framework.
272 run_in_debugger -- if true, run this test under the pdb debugger.
274 if id is None: # generate id if required
275 id = self.next_test_case_id
276 self.next_test_case_id += 1
277 # test identifier will become suiteid.testid
278 tcid = '%s: %s' % (self.id, id)
279 # suite_settings may be passed as a parameter;
280 # if not, set from attribute:
281 kwargs.setdefault('suite_settings', self.suite_settings)
282 # generate and add test case
283 tc = test_case_class(method_name, input, expected, tcid,
284 run_in_debugger=run_in_debugger, **kwargs)
285 self.addTest(tc)
286 return tc
288 def generate_no_tests(self, *args, **kwargs):
289 pass
292 class TransformTestCase(CustomTestCase):
295 Output checker for the transform.
297 Should probably be called TransformOutputChecker, but I can deal with
298 that later when/if someone comes up with a category of transform test
299 cases that have nothing to do with the input and output of the transform.
302 option_parser = frontend.OptionParser(components=(rst.Parser,))
303 settings = option_parser.get_default_values()
304 settings.report_level = 1
305 settings.halt_level = 5
306 settings.debug = package_unittest.debug
307 settings.warning_stream = DevNull()
308 unknown_reference_resolvers = ()
310 def __init__(self, *args, **kwargs):
311 self.transforms = kwargs['transforms']
312 """List of transforms to perform for this test case."""
314 self.parser = kwargs['parser']
315 """Input parser for this test case."""
317 del kwargs['transforms'], kwargs['parser'] # only wanted here
318 CustomTestCase.__init__(self, *args, **kwargs)
320 def supports(self, format):
321 return 1
323 def test_transforms(self):
324 if self.run_in_debugger:
325 pdb.set_trace()
326 settings = self.settings.copy()
327 settings.__dict__.update(self.suite_settings)
328 document = utils.new_document('test data', settings)
329 self.parser.parse(self.input, document)
330 # Don't do a ``populate_from_components()`` because that would
331 # enable the Transformer's default transforms.
332 document.transformer.add_transforms(self.transforms)
333 document.transformer.add_transform(universal.TestMessages)
334 document.transformer.components['writer'] = self
335 document.transformer.apply_transforms()
336 output = document.pformat()
337 self.compare_output(self.input, output, self.expected)
339 def test_transforms_verbosely(self):
340 if self.run_in_debugger:
341 pdb.set_trace()
342 print '\n', self.id
343 print '-' * 70
344 print self.input
345 settings = self.settings.copy()
346 settings.__dict__.update(self.suite_settings)
347 document = utils.new_document('test data', settings)
348 self.parser.parse(self.input, document)
349 print '-' * 70
350 print document.pformat()
351 for transformClass in self.transforms:
352 transformClass(document).apply()
353 output = document.pformat()
354 print '-' * 70
355 print output
356 self.compare_output(self.input, output, self.expected)
359 class TransformTestSuite(CustomTestSuite):
362 A collection of TransformTestCases.
364 A TransformTestSuite instance manufactures TransformTestCases,
365 keeps track of them, and provides a shared test fixture (a-la
366 setUp and tearDown).
369 def __init__(self, parser, suite_settings=None):
370 self.parser = parser
371 """Parser shared by all test cases."""
373 CustomTestSuite.__init__(self, suite_settings=suite_settings)
375 def generateTests(self, dict, dictname='totest',
376 testmethod='test_transforms'):
378 Stock the suite with test cases generated from a test data dictionary.
380 Each dictionary key (test type's name) maps to a tuple, whose
381 first item is a list of transform classes and whose second
382 item is a list of tests. Each test is a list: input, expected
383 output, optional modifier. The optional third entry, a
384 behavior modifier, can be 0 (temporarily disable this test) or
385 1 (run this test under the pdb debugger). Tests should be
386 self-documenting and not require external comments.
388 for name, (transforms, cases) in dict.items():
389 for casenum in range(len(cases)):
390 case = cases[casenum]
391 run_in_debugger = 0
392 if len(case)==3:
393 # TODO: (maybe) change the 3rd argument to a dict, so it
394 # can handle more cases by keyword ('disable', 'debug',
395 # 'settings'), here and in other generateTests methods.
396 # But there's also the method that
397 # HtmlPublishPartsTestSuite uses <DJG>
398 if case[2]:
399 run_in_debugger = 1
400 else:
401 continue
402 self.addTestCase(
403 TransformTestCase, testmethod,
404 transforms=transforms, parser=self.parser,
405 input=case[0], expected=case[1],
406 id='%s[%r][%s]' % (dictname, name, casenum),
407 run_in_debugger=run_in_debugger)
410 class ParserTestCase(CustomTestCase):
413 Output checker for the parser.
415 Should probably be called ParserOutputChecker, but I can deal with
416 that later when/if someone comes up with a category of parser test
417 cases that have nothing to do with the input and output of the parser.
420 parser = rst.Parser()
421 """Parser shared by all ParserTestCases."""
423 option_parser = frontend.OptionParser(components=(rst.Parser,))
424 settings = option_parser.get_default_values()
425 settings.report_level = 5
426 settings.halt_level = 5
427 settings.debug = package_unittest.debug
429 def test_parser(self):
430 if self.run_in_debugger:
431 pdb.set_trace()
432 settings = self.settings.copy()
433 settings.__dict__.update(self.suite_settings)
434 document = utils.new_document('test data', settings)
435 self.parser.parse(self.input, document)
436 output = document.pformat()
437 self.compare_output(self.input, output, self.expected)
440 class ParserTestSuite(CustomTestSuite):
443 A collection of ParserTestCases.
445 A ParserTestSuite instance manufactures ParserTestCases,
446 keeps track of them, and provides a shared test fixture (a-la
447 setUp and tearDown).
450 test_case_class = ParserTestCase
452 def generateTests(self, dict, dictname='totest'):
454 Stock the suite with test cases generated from a test data dictionary.
456 Each dictionary key (test type name) maps to a list of tests. Each
457 test is a list: input, expected output, optional modifier. The
458 optional third entry, a behavior modifier, can be 0 (temporarily
459 disable this test) or 1 (run this test under the pdb debugger). Tests
460 should be self-documenting and not require external comments.
462 for name, cases in dict.items():
463 for casenum in range(len(cases)):
464 case = cases[casenum]
465 run_in_debugger = 0
466 if len(case)==3:
467 if case[2]:
468 run_in_debugger = 1
469 else:
470 continue
471 self.addTestCase(
472 self.test_case_class, 'test_parser',
473 input=case[0], expected=case[1],
474 id='%s[%r][%s]' % (dictname, name, casenum),
475 run_in_debugger=run_in_debugger)
478 class PEPParserTestCase(ParserTestCase):
480 """PEP-specific parser test case."""
482 parser = rst.Parser(rfc2822=1, inliner=rst.states.Inliner())
483 """Parser shared by all PEPParserTestCases."""
485 option_parser = frontend.OptionParser(components=(rst.Parser, pep.Reader))
486 settings = option_parser.get_default_values()
487 settings.report_level = 5
488 settings.halt_level = 5
489 settings.debug = package_unittest.debug
492 class PEPParserTestSuite(ParserTestSuite):
494 """A collection of PEPParserTestCases."""
496 test_case_class = PEPParserTestCase
499 class GridTableParserTestCase(CustomTestCase):
501 parser = tableparser.GridTableParser()
503 def test_parse_table(self):
504 self.parser.setup(StringList(string2lines(self.input), 'test data'))
505 try:
506 self.parser.find_head_body_sep()
507 self.parser.parse_table()
508 output = self.parser.cells
509 except Exception, details:
510 output = '%s: %s' % (details.__class__.__name__, details)
511 self.compare_output(self.input, pformat(output) + '\n',
512 pformat(self.expected) + '\n')
514 def test_parse(self):
515 try:
516 output = self.parser.parse(StringList(string2lines(self.input),
517 'test data'))
518 except Exception, details:
519 output = '%s: %s' % (details.__class__.__name__, details)
520 self.compare_output(self.input, pformat(output) + '\n',
521 pformat(self.expected) + '\n')
524 class GridTableParserTestSuite(CustomTestSuite):
527 A collection of GridTableParserTestCases.
529 A GridTableParserTestSuite instance manufactures GridTableParserTestCases,
530 keeps track of them, and provides a shared test fixture (a-la setUp and
531 tearDown).
534 test_case_class = GridTableParserTestCase
536 def generateTests(self, dict, dictname='totest'):
538 Stock the suite with test cases generated from a test data dictionary.
540 Each dictionary key (test type name) maps to a list of tests. Each
541 test is a list: an input table, expected output from parse_table(),
542 expected output from parse(), optional modifier. The optional fourth
543 entry, a behavior modifier, can be 0 (temporarily disable this test)
544 or 1 (run this test under the pdb debugger). Tests should be
545 self-documenting and not require external comments.
547 for name, cases in dict.items():
548 for casenum in range(len(cases)):
549 case = cases[casenum]
550 run_in_debugger = 0
551 if len(case) == 4:
552 if case[-1]:
553 run_in_debugger = 1
554 else:
555 continue
556 self.addTestCase(self.test_case_class, 'test_parse_table',
557 input=case[0], expected=case[1],
558 id='%s[%r][%s]' % (dictname, name, casenum),
559 run_in_debugger=run_in_debugger)
560 self.addTestCase(self.test_case_class, 'test_parse',
561 input=case[0], expected=case[2],
562 id='%s[%r][%s]' % (dictname, name, casenum),
563 run_in_debugger=run_in_debugger)
566 class SimpleTableParserTestCase(GridTableParserTestCase):
568 parser = tableparser.SimpleTableParser()
571 class SimpleTableParserTestSuite(CustomTestSuite):
574 A collection of SimpleTableParserTestCases.
577 test_case_class = SimpleTableParserTestCase
579 def generateTests(self, dict, dictname='totest'):
581 Stock the suite with test cases generated from a test data dictionary.
583 Each dictionary key (test type name) maps to a list of tests. Each
584 test is a list: an input table, expected output from parse(), optional
585 modifier. The optional third entry, a behavior modifier, can be 0
586 (temporarily disable this test) or 1 (run this test under the pdb
587 debugger). Tests should be self-documenting and not require external
588 comments.
590 for name, cases in dict.items():
591 for casenum in range(len(cases)):
592 case = cases[casenum]
593 run_in_debugger = 0
594 if len(case) == 3:
595 if case[-1]:
596 run_in_debugger = 1
597 else:
598 continue
599 self.addTestCase(self.test_case_class, 'test_parse',
600 input=case[0], expected=case[1],
601 id='%s[%r][%s]' % (dictname, name, casenum),
602 run_in_debugger=run_in_debugger)
605 class PythonModuleParserTestCase(CustomTestCase):
607 def test_parser(self):
608 if self.run_in_debugger:
609 pdb.set_trace()
610 module = moduleparser.parse_module(self.input, 'test data').pformat()
611 output = str(module)
612 self.compare_output(self.input, output, self.expected)
614 def test_token_parser_rhs(self):
615 if self.run_in_debugger:
616 pdb.set_trace()
617 tr = moduleparser.TokenParser(self.input)
618 output = tr.rhs(1)
619 self.compare_output(self.input, output, self.expected)
622 class PythonModuleParserTestSuite(CustomTestSuite):
625 A collection of PythonModuleParserTestCase.
628 notified = None
630 def __init__(self, *args, **kwargs):
631 if moduleparser is None:
632 if not self.notified:
633 print ('Tests of docutils.readers.python skipped; '
634 'Python 2.2 or higher required.')
635 PythonModuleParserTestSuite.notified = 1
636 self.generateTests = self.generate_no_tests
637 CustomTestSuite.__init__(self, *args, **kwargs)
639 def generateTests(self, dict, dictname='totest',
640 testmethod='test_parser'):
642 Stock the suite with test cases generated from a test data dictionary.
644 Each dictionary key (test type's name) maps to a list of tests. Each
645 test is a list: input, expected output, optional modifier. The
646 optional third entry, a behavior modifier, can be 0 (temporarily
647 disable this test) or 1 (run this test under the pdb debugger). Tests
648 should be self-documenting and not require external comments.
650 for name, cases in dict.items():
651 for casenum in range(len(cases)):
652 case = cases[casenum]
653 run_in_debugger = 0
654 if len(case)==3:
655 if case[2]:
656 run_in_debugger = 1
657 else:
658 continue
659 self.addTestCase(
660 PythonModuleParserTestCase, testmethod,
661 input=case[0], expected=case[1],
662 id='%s[%r][%s]' % (dictname, name, casenum),
663 run_in_debugger=run_in_debugger)
666 class WriterPublishTestCase(CustomTestCase, docutils.SettingsSpec):
669 Test case for publish.
672 settings_default_overrides = {'_disable_config': 1,
673 'strict_visitor': 1}
674 writer_name = '' # set in subclasses or constructor
676 def __init__(self, *args, **kwargs):
677 if kwargs.has_key('writer_name'):
678 self.writer_name = kwargs['writer_name']
679 del kwargs['writer_name']
680 CustomTestCase.__init__(self, *args, **kwargs)
682 def test_publish(self):
683 if self.run_in_debugger:
684 pdb.set_trace()
685 output = docutils.core.publish_string(
686 source=self.input,
687 reader_name='standalone',
688 parser_name='restructuredtext',
689 writer_name=self.writer_name,
690 settings_spec=self,
691 settings_overrides=self.suite_settings)
692 self.compare_output(self.input, output, self.expected)
695 class PublishTestSuite(CustomTestSuite):
697 def __init__(self, writer_name, suite_settings=None):
699 `writer_name` is the name of the writer to use.
701 CustomTestSuite.__init__(self, suite_settings=suite_settings)
702 self.test_class = WriterPublishTestCase
703 self.writer_name = writer_name
705 def generateTests(self, dict, dictname='totest'):
706 for name, cases in dict.items():
707 for casenum in range(len(cases)):
708 case = cases[casenum]
709 run_in_debugger = 0
710 if len(case)==3:
711 if case[2]:
712 run_in_debugger = 1
713 else:
714 continue
715 self.addTestCase(
716 self.test_class, 'test_publish',
717 input=case[0], expected=case[1],
718 id='%s[%r][%s]' % (dictname, name, casenum),
719 run_in_debugger=run_in_debugger,
720 # Passed to constructor of self.test_class:
721 writer_name=self.writer_name)
724 class HtmlPublishPartsTestSuite(CustomTestSuite):
726 def generateTests(self, dict, dictname='totest'):
727 for name, (settings_overrides, cases) in dict.items():
728 settings = self.suite_settings.copy()
729 settings.update(settings_overrides)
730 for casenum in range(len(cases)):
731 case = cases[casenum]
732 run_in_debugger = 0
733 if len(case)==3:
734 if case[2]:
735 run_in_debugger = 1
736 else:
737 continue
738 self.addTestCase(
739 HtmlWriterPublishPartsTestCase, 'test_publish',
740 input=case[0], expected=case[1],
741 id='%s[%r][%s]' % (dictname, name, casenum),
742 run_in_debugger=run_in_debugger,
743 suite_settings=settings)
746 class HtmlWriterPublishPartsTestCase(WriterPublishTestCase):
749 Test case for HTML writer via the publish_parts interface.
752 writer_name = 'html'
754 settings_default_overrides = \
755 WriterPublishTestCase.settings_default_overrides.copy()
756 settings_default_overrides['stylesheet'] = ''
758 def test_publish(self):
759 if self.run_in_debugger:
760 pdb.set_trace()
761 parts = docutils.core.publish_parts(
762 source=self.input,
763 reader_name='standalone',
764 parser_name='restructuredtext',
765 writer_name=self.writer_name,
766 settings_spec=self,
767 settings_overrides=self.suite_settings)
768 output = self.format_output(parts)
769 # interpolate standard variables:
770 expected = self.expected % {'version': docutils.__version__}
771 self.compare_output(self.input, output, expected)
774 standard_content_type_template = ('<meta http-equiv="Content-Type"'
775 ' content="text/html; charset=%s" />\n')
776 standard_generator_template = (
777 '<meta name="generator"'
778 ' content="Docutils %s: http://docutils.sourceforge.net/" />\n')
779 standard_html_meta_value = (
780 standard_content_type_template
781 + standard_generator_template % docutils.__version__)
782 standard_meta_value = standard_html_meta_value % 'utf-8'
783 standard_html_prolog = """\
784 <?xml version="1.0" encoding="%s" ?>
785 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
788 def format_output(self, parts):
789 """Minimize & standardize the output."""
790 # remove redundant parts:
791 del parts['whole']
792 assert parts['body'] == parts['fragment']
793 del parts['body']
794 # remove standard portions:
795 parts['meta'] = parts['meta'].replace(self.standard_meta_value, '')
796 parts['html_head'] = parts['html_head'].replace(
797 self.standard_html_meta_value, '...')
798 parts['html_prolog'] = parts['html_prolog'].replace(
799 self.standard_html_prolog, '')
800 # remove empty values:
801 for key in parts.keys():
802 if not parts[key]:
803 del parts[key]
804 # standard output format:
805 keys = parts.keys()
806 keys.sort()
807 output = []
808 for key in keys:
809 output.append("%r: '''%s'''"
810 % (key, parts[key].encode('raw_unicode_escape')))
811 if output[-1].endswith("\n'''"):
812 output[-1] = output[-1][:-4] + "\\n'''"
813 return '{' + ',\n '.join(output) + '}\n'
816 def exception_data(code):
818 Execute `code` and return the resulting exception, the exception arguments,
819 and the formatted exception string.
821 try:
822 exec(code)
823 except Exception, detail:
824 return (detail, detail.args,
825 '%s: %s' % (detail.__class__.__name__, detail))
828 def _format_str(*args):
829 r"""
830 Return a tuple containing representations of all args.
832 Same as map(repr, args) except that it returns multi-line
833 representations for strings containing newlines, e.g.::
835 '''\
836 foo \n\
839 baz'''
841 instead of::
843 'foo \nbar\n\nbaz'
845 This is a helper function for CustomTestCase.
847 import re
848 return_tuple = []
849 for i in args:
850 r = repr(i)
851 if ( (isinstance(i, StringType) or isinstance(i, UnicodeType))
852 and '\n' in i):
853 stripped = ''
854 if isinstance(i, UnicodeType):
855 # stripped = 'u' or 'U'
856 stripped = r[0]
857 r = r[1:]
858 # quote_char = "'" or '"'
859 quote_char = r[0]
860 assert quote_char in ("'", '"')
861 assert r[0] == r[-1]
862 r = r[1:-1]
863 r = (stripped + 3 * quote_char + '\\\n' +
864 re.sub(r'(?<!\\)((\\\\)*)\\n', r'\1\n', r) +
865 3 * quote_char)
866 r = re.sub(r' \n', r' \\n\\\n', r)
867 return_tuple.append(r)
868 return tuple(return_tuple)