comparison env/lib/python3.9/site-packages/docutils/utils/error_reporting.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # :Id: $Id: error_reporting.py 8367 2019-08-27 12:09:56Z milde $
5 # :Copyright: © 2011 Günter Milde.
6 # :License: Released under the terms of the `2-Clause BSD license`_, in short:
7 #
8 # Copying and distribution of this file, with or without modification,
9 # are permitted in any medium without royalty provided the copyright
10 # notice and this notice are preserved.
11 # This file is offered as-is, without any warranty.
12 #
13 # .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause
14
15 """
16 Error reporting should be safe from encoding/decoding errors.
17 However, implicit conversions of strings and exceptions like
18
19 >>> u'%s world: %s' % ('H\xe4llo', Exception(u'H\xe4llo')
20
21 fail in some Python versions:
22
23 * In Python <= 2.6, ``unicode(<exception instance>)`` uses
24 `__str__` and fails with non-ASCII chars in`unicode` arguments.
25 (work around http://bugs.python.org/issue2517):
26
27 * In Python 2, unicode(<exception instance>) fails, with non-ASCII
28 chars in arguments. (Use case: in some locales, the errstr
29 argument of IOError contains non-ASCII chars.)
30
31 * In Python 2, str(<exception instance>) fails, with non-ASCII chars
32 in `unicode` arguments.
33
34 The `SafeString`, `ErrorString` and `ErrorOutput` classes handle
35 common exceptions.
36 """
37
38 import codecs
39 import sys
40
41 # Guess the locale's encoding.
42 # If no valid guess can be made, locale_encoding is set to `None`:
43 try:
44 import locale # module missing in Jython
45 except ImportError:
46 locale_encoding = None
47 else:
48 try:
49 locale_encoding = locale.getlocale()[1] or locale.getdefaultlocale()[1]
50 # locale.getpreferredencoding([do_setlocale=True|False])
51 # has side-effects | might return a wrong guess.
52 # (cf. Update 1 in http://stackoverflow.com/questions/4082645/using-python-2-xs-locale-module-to-format-numbers-and-currency)
53 except ValueError as error: # OS X may set UTF-8 without language code
54 # see http://bugs.python.org/issue18378
55 # and https://sourceforge.net/p/docutils/bugs/298/
56 if "unknown locale: UTF-8" in error.args:
57 locale_encoding = "UTF-8"
58 else:
59 locale_encoding = None
60 except: # any other problems determining the locale -> use None
61 locale_encoding = None
62 try:
63 codecs.lookup(locale_encoding or '') # None -> ''
64 except LookupError:
65 locale_encoding = None
66
67
68 if sys.version_info >= (3, 0):
69 unicode = str # noqa
70
71
72 class SafeString(object):
73 """
74 A wrapper providing robust conversion to `str` and `unicode`.
75 """
76
77 def __init__(self, data, encoding=None, encoding_errors='backslashreplace',
78 decoding_errors='replace'):
79 self.data = data
80 self.encoding = (encoding or getattr(data, 'encoding', None) or
81 locale_encoding or 'ascii')
82 self.encoding_errors = encoding_errors
83 self.decoding_errors = decoding_errors
84
85
86 def __str__(self):
87 try:
88 return str(self.data)
89 except UnicodeEncodeError:
90 if isinstance(self.data, Exception):
91 args = [str(SafeString(arg, self.encoding,
92 self.encoding_errors))
93 for arg in self.data.args]
94 return ', '.join(args)
95 if isinstance(self.data, unicode):
96 if sys.version_info > (3, 0):
97 return self.data
98 else:
99 return self.data.encode(self.encoding,
100 self.encoding_errors)
101 raise
102
103 def __unicode__(self):
104 """
105 Return unicode representation of `self.data`.
106
107 Try ``unicode(self.data)``, catch `UnicodeError` and
108
109 * if `self.data` is an Exception instance, work around
110 http://bugs.python.org/issue2517 with an emulation of
111 Exception.__unicode__,
112
113 * else decode with `self.encoding` and `self.decoding_errors`.
114 """
115 try:
116 u = unicode(self.data)
117 if isinstance(self.data, EnvironmentError):
118 u = u.replace(": u'", ": '") # normalize filename quoting
119 return u
120 except UnicodeError as error: # catch ..Encode.. and ..Decode.. errors
121 if isinstance(self.data, EnvironmentError):
122 return u"[Errno %s] %s: '%s'" % (self.data.errno,
123 SafeString(self.data.strerror, self.encoding,
124 self.decoding_errors),
125 SafeString(self.data.filename, self.encoding,
126 self.decoding_errors))
127 if isinstance(self.data, Exception):
128 args = [unicode(SafeString(arg, self.encoding,
129 decoding_errors=self.decoding_errors))
130 for arg in self.data.args]
131 return u', '.join(args)
132 if isinstance(error, UnicodeDecodeError):
133 return unicode(self.data, self.encoding, self.decoding_errors)
134 raise
135
136 class ErrorString(SafeString):
137 """
138 Safely report exception type and message.
139 """
140 def __str__(self):
141 return '%s: %s' % (self.data.__class__.__name__,
142 super(ErrorString, self).__str__())
143
144 def __unicode__(self):
145 return u'%s: %s' % (self.data.__class__.__name__,
146 super(ErrorString, self).__unicode__())
147
148
149 class ErrorOutput(object):
150 """
151 Wrapper class for file-like error streams with
152 failsave de- and encoding of `str`, `bytes`, `unicode` and
153 `Exception` instances.
154 """
155
156 def __init__(self, stream=None, encoding=None,
157 encoding_errors='backslashreplace',
158 decoding_errors='replace'):
159 """
160 :Parameters:
161 - `stream`: a file-like object,
162 a string (path to a file),
163 `None` (write to `sys.stderr`, default), or
164 evaluating to `False` (write() requests are ignored).
165 - `encoding`: `stream` text encoding. Guessed if None.
166 - `encoding_errors`: how to treat encoding errors.
167 """
168 if stream is None:
169 stream = sys.stderr
170 elif not(stream):
171 stream = False
172 # if `stream` is a file name, open it
173 elif isinstance(stream, str):
174 stream = open(stream, 'w')
175 elif isinstance(stream, unicode):
176 stream = open(stream.encode(sys.getfilesystemencoding()), 'w')
177
178 self.stream = stream
179 """Where warning output is sent."""
180
181 self.encoding = (encoding or getattr(stream, 'encoding', None) or
182 locale_encoding or 'ascii')
183 """The output character encoding."""
184
185 self.encoding_errors = encoding_errors
186 """Encoding error handler."""
187
188 self.decoding_errors = decoding_errors
189 """Decoding error handler."""
190
191 def write(self, data):
192 """
193 Write `data` to self.stream. Ignore, if self.stream is False.
194
195 `data` can be a `string`, `unicode`, or `Exception` instance.
196 """
197 if self.stream is False:
198 return
199 if isinstance(data, Exception):
200 data = unicode(SafeString(data, self.encoding,
201 self.encoding_errors, self.decoding_errors))
202 try:
203 self.stream.write(data)
204 except UnicodeEncodeError:
205 self.stream.write(data.encode(self.encoding, self.encoding_errors))
206 except TypeError:
207 if isinstance(data, unicode): # passed stream may expect bytes
208 self.stream.write(data.encode(self.encoding,
209 self.encoding_errors))
210 return
211 if self.stream in (sys.stderr, sys.stdout):
212 self.stream.buffer.write(data) # write bytes to raw stream
213 else:
214 self.stream.write(unicode(data, self.encoding,
215 self.decoding_errors))
216
217 def close(self):
218 """
219 Close the error-output stream.
220
221 Ignored if the stream is` sys.stderr` or `sys.stdout` or has no
222 close() method.
223 """
224 if self.stream in (sys.stdout, sys.stderr):
225 return
226 try:
227 self.stream.close()
228 except AttributeError:
229 pass