* Consolidated version numbering documentation in docs/dev/policies.txt (moved text...
[docutils.git] / docutils / test / test_error_reporting.py
blobc0615d07b8c2d680540a0aed364bc7644f3d0910
1 #! /usr/bin/env python
2 # .. coding: utf-8
3 # $Id$
4 # Author: Günter Milde <milde@users.sourceforge.net>
5 # Copyright: This module has been placed in the public domain.
7 """
8 Test `EnvironmentError` reporting.
10 In some locales, the `errstr` argument of IOError and OSError contains
11 non-ASCII chars.
13 In Python 2, converting an exception instance to `str` or `unicode`
14 might fail, with non-ASCII chars in arguments and the default encoding
15 and errors ('ascii', 'strict').
17 Therefore, Docutils must not use string interpolation with exception
18 instances like, e.g., ::
20 try:
21 something
22 except IOError, error:
23 print 'Found %s' % error
25 unless the minimal required Python version has this problem fixed.
26 """
28 import unittest
29 import sys, os
30 import codecs
31 try: # from standard library module `io`
32 from io import StringIO, BytesIO
33 except ImportError: # new in Python 2.6
34 from StringIO import StringIO
35 BytesIO = StringIO
37 import DocutilsTestSupport # must be imported before docutils
38 from docutils import core, parsers, frontend, utils
39 from docutils.utils.error_reporting import SafeString, ErrorString, ErrorOutput
41 oldlocale = None
42 if sys.version_info < (3,0): # problems solved in py3k
43 try:
44 import locale # module missing in Jython
45 oldlocale = locale.getlocale()
46 # Why does getlocale return the defaultlocale in Python 3.2 ????
47 # oldlocale = (None, None) # test suite runs without locale
48 except ImportError:
49 print ('cannot test error reporting with problematic locales,\n'
50 '`import locale` failed.')
53 # locales confirmed to use non-ASCII chars in the IOError message
54 # for a missing file (https://bugs.gentoo.org/show_bug.cgi?id=349101)
55 # TODO: add more confirmed problematic locales
56 problematic_locales = ['cs_CZ', 'cs_CZ.UTF8',
57 'el_GR', 'el_GR.UTF-8',
58 # 'fr_FR.UTF-8', # only OSError
59 'ja_JP.UTF-8',
60 'ru_RU', 'ru_RU.KOI8-R',
61 'ru_RU.UTF-8',
62 '', # default locale: might be non-problematic
65 if oldlocale is not None:
66 # find a supported problematic locale:
67 for testlocale in problematic_locales:
68 try:
69 locale.setlocale(locale.LC_ALL, testlocale)
70 except locale.Error:
71 testlocale = None
72 else:
73 break
74 locale.setlocale(locale.LC_ALL, oldlocale) # reset
75 else:
76 testlocale = None
78 class SafeStringTests(unittest.TestCase):
79 # the error message in EnvironmentError instances comes from the OS
80 # and in some locales (e.g. ru_RU), contains high bit chars.
81 # -> see the test in test_error_reporting.py
83 # test data:
84 bs = b'\xfc' # unicode(bs) fails, str(bs) in Python 3 return repr()
85 us = u'\xfc' # bytes(us) fails; str(us) fails in Python 2
86 be = Exception(bs) # unicode(be) fails
87 ue = Exception(us) # bytes(ue) fails, str(ue) fails in Python 2;
88 # unicode(ue) fails in Python < 2.6 (issue2517_)
89 # .. _issue2517: http://bugs.python.org/issue2517
90 # wrapped test data:
91 wbs = SafeString(bs)
92 wus = SafeString(us)
93 wbe = SafeString(be)
94 wue = SafeString(ue)
96 def test_7bit(self):
97 # wrapping (not required with 7-bit chars) must not change the
98 # result of conversions:
99 bs7 = b'foo'
100 us7 = u'foo'
101 be7 = Exception(bs7)
102 ue7 = Exception(us7)
103 self.assertEqual(str(42), str(SafeString(42)))
104 self.assertEqual(str(bs7), str(SafeString(bs7)))
105 self.assertEqual(str(us7), str(SafeString(us7)))
106 self.assertEqual(str(be7), str(SafeString(be7)))
107 self.assertEqual(str(ue7), str(SafeString(ue7)))
108 self.assertEqual(unicode(7), unicode(SafeString(7)))
109 self.assertEqual(unicode(bs7), unicode(SafeString(bs7)))
110 self.assertEqual(unicode(us7), unicode(SafeString(us7)))
111 self.assertEqual(unicode(be7), unicode(SafeString(be7)))
112 self.assertEqual(unicode(ue7), unicode(SafeString(ue7)))
114 def test_ustr(self):
115 """Test conversion to a unicode-string."""
116 # unicode(self.bs) fails
117 self.assertEqual(unicode, type(unicode(self.wbs)))
118 self.assertEqual(unicode(self.us), unicode(self.wus))
119 # unicode(self.be) fails
120 self.assertEqual(unicode, type(unicode(self.wbe)))
121 # unicode(ue) fails in Python < 2.6 (issue2517_)
122 self.assertEqual(unicode, type(unicode(self.wue)))
123 self.assertEqual(self.us, unicode(self.wue))
125 def test_str(self):
126 """Test conversion to a string (bytes in Python 2, unicode in Python 3)."""
127 self.assertEqual(str(self.bs), str(self.wbs))
128 self.assertEqual(str(self.be), str(self.be))
129 # str(us) fails in Python 2
130 self.assertEqual(str, type(str(self.wus)))
131 # str(ue) fails in Python 2
132 self.assertEqual(str, type(str(self.wue)))
135 class ErrorStringTests(unittest.TestCase):
136 bs = b'\xfc' # unicode(bs) fails, str(bs) in Python 3 return repr()
137 us = u'\xfc' # bytes(us) fails; str(us) fails in Python 2
139 def test_str(self):
140 self.assertEqual('Exception: spam',
141 str(ErrorString(Exception('spam'))))
142 self.assertEqual('IndexError: '+str(self.bs),
143 str(ErrorString(IndexError(self.bs))))
144 self.assertEqual('ImportError: %s' % SafeString(self.us),
145 str(ErrorString(ImportError(self.us))))
147 def test_unicode(self):
148 self.assertEqual(u'Exception: spam',
149 unicode(ErrorString(Exception(u'spam'))))
150 self.assertEqual(u'IndexError: '+self.us,
151 unicode(ErrorString(IndexError(self.us))))
152 self.assertEqual(u'ImportError: %s' % SafeString(self.bs),
153 unicode(ErrorString(ImportError(self.bs))))
156 # ErrorOutput tests
157 # -----------------
159 # Stub: Buffer with 'strict' auto-conversion of input to byte string:
160 class BBuf(BytesIO):
161 def write(self, data):
162 if isinstance(data, unicode):
163 data.encode('ascii', 'strict')
164 super(BBuf, self).write(data)
166 # Stub: Buffer expecting unicode string:
167 class UBuf(StringIO):
168 def write(self, data):
169 # emulate Python 3 handling of stdout, stderr
170 if isinstance(data, bytes):
171 raise TypeError('must be unicode, not bytes')
172 super(UBuf, self).write(data)
174 class ErrorOutputTests(unittest.TestCase):
175 def test_defaults(self):
176 e = ErrorOutput()
177 self.assertEqual(e.stream, sys.stderr)
179 def test_bbuf(self):
180 buf = BBuf() # buffer storing byte string
181 e = ErrorOutput(buf, encoding='ascii')
182 # write byte-string as-is
183 e.write(b'b\xfc')
184 self.assertEqual(buf.getvalue(), b'b\xfc')
185 # encode unicode data with backslashescape fallback replacement:
186 e.write(u' u\xfc')
187 self.assertEqual(buf.getvalue(), b'b\xfc u\\xfc')
188 # handle Exceptions with Unicode string args
189 # unicode(Exception(u'e\xfc')) # fails in Python < 2.6
190 e.write(AttributeError(u' e\xfc'))
191 self.assertEqual(buf.getvalue(), b'b\xfc u\\xfc e\\xfc')
192 # encode with `encoding` attribute
193 e.encoding = 'utf8'
194 e.write(u' u\xfc')
195 self.assertEqual(buf.getvalue(), b'b\xfc u\\xfc e\\xfc u\xc3\xbc')
197 def test_ubuf(self):
198 buf = UBuf() # buffer only accepting unicode string
199 # decode of binary strings
200 e = ErrorOutput(buf, encoding='ascii')
201 e.write(b'b\xfc')
202 self.assertEqual(buf.getvalue(), u'b\ufffd') # use REPLACEMENT CHARACTER
203 # write Unicode string and Exceptions with Unicode args
204 e.write(u' u\xfc')
205 self.assertEqual(buf.getvalue(), u'b\ufffd u\xfc')
206 e.write(AttributeError(u' e\xfc'))
207 self.assertEqual(buf.getvalue(), u'b\ufffd u\xfc e\xfc')
208 # decode with `encoding` attribute
209 e.encoding = 'latin1'
210 e.write(b' b\xfc')
211 self.assertEqual(buf.getvalue(), u'b\ufffd u\xfc e\xfc b\xfc')
215 class SafeStringTests_locale(unittest.TestCase):
217 Test docutils.SafeString with 'problematic' locales.
219 The error message in `EnvironmentError` instances comes from the OS
220 and in some locales (e.g. ru_RU), contains high bit chars.
222 if testlocale:
223 locale.setlocale(locale.LC_ALL, testlocale)
224 # test data:
225 bs = b'\xfc'
226 us = u'\xfc'
227 try:
228 open(b'\xfc')
229 except IOError, e: # in Python 3 the name for the exception instance
230 bioe = e # is local to the except clause
231 try:
232 open(u'\xfc')
233 except IOError, e:
234 uioe = e
235 except UnicodeEncodeError:
236 try:
237 open(u'\xfc'.encode(sys.getfilesystemencoding(), 'replace'))
238 except IOError, e:
239 uioe = e
240 try:
241 os.chdir(b'\xfc')
242 except OSError, e:
243 bose = e
244 try:
245 os.chdir(u'\xfc')
246 except OSError, e:
247 uose = e
248 except UnicodeEncodeError:
249 try:
250 os.chdir(u'\xfc'.encode(sys.getfilesystemencoding(), 'replace'))
251 except OSError, e:
252 uose = e
253 # wrapped test data:
254 wbioe = SafeString(bioe)
255 wuioe = SafeString(uioe)
256 wbose = SafeString(bose)
257 wuose = SafeString(uose)
258 # reset locale
259 if testlocale:
260 locale.setlocale(locale.LC_ALL, oldlocale)
262 def test_ustr(self):
263 """Test conversion to a unicode-string."""
264 # unicode(bioe) fails with e.g. 'ru_RU.utf8' locale
265 self.assertEqual(unicode, type(unicode(self.wbioe)))
266 self.assertEqual(unicode, type(unicode(self.wuioe)))
267 self.assertEqual(unicode, type(unicode(self.wbose)))
268 self.assertEqual(unicode, type(unicode(self.wuose)))
270 def test_str(self):
271 """Test conversion to a string (bytes in Python 2, unicode in Python 3)."""
272 self.assertEqual(str(self.bioe), str(self.wbioe))
273 self.assertEqual(str(self.uioe), str(self.wuioe))
274 self.assertEqual(str(self.bose), str(self.wbose))
275 self.assertEqual(str(self.uose), str(self.wuose))
279 class ErrorReportingTests(unittest.TestCase):
281 Test cases where error reporting can go wrong.
283 Do not test the exact output (as this varies with the locale), just
284 ensure that the correct exception is thrown.
287 # These tests fail with a 'problematic locale',
288 # Docutils revision < 7035, and Python 2:
290 parser = parsers.rst.Parser()
291 """Parser shared by all ParserTestCases."""
293 option_parser = frontend.OptionParser(components=(parsers.rst.Parser,))
294 settings = option_parser.get_default_values()
295 settings.report_level = 1
296 settings.halt_level = 1
297 settings.warning_stream = ''
298 document = utils.new_document('test data', settings)
300 def setUp(self):
301 if testlocale:
302 locale.setlocale(locale.LC_ALL, testlocale)
304 def tearDown(self):
305 if testlocale:
306 locale.setlocale(locale.LC_ALL, oldlocale)
308 def test_include(self):
309 source = ('.. include:: bogus.txt')
310 self.assertRaises(utils.SystemMessage,
311 self.parser.parse, source, self.document)
313 def test_raw_file(self):
314 source = ('.. raw:: html\n'
315 ' :file: bogus.html\n')
316 self.assertRaises(utils.SystemMessage,
317 self.parser.parse, source, self.document)
319 def test_csv_table(self):
320 source = ('.. csv-table:: external file\n'
321 ' :file: bogus.csv\n')
322 self.assertRaises(utils.SystemMessage,
323 self.parser.parse, source, self.document)
325 if __name__ == '__main__':
326 unittest.main()