Show More
@@ -28,12 +28,15 b' import abc' | |||||
28 | import sys |
|
28 | import sys | |
29 | import warnings |
|
29 | import warnings | |
30 |
|
30 | |||
|
31 | from IPython.external.decorator import decorator | |||
|
32 | ||||
31 | # Our own imports |
|
33 | # Our own imports | |
32 | from IPython.config.configurable import Configurable |
|
34 | from IPython.config.configurable import Configurable | |
33 | from IPython.lib import pretty |
|
35 | from IPython.lib import pretty | |
34 | from IPython.utils.traitlets import ( |
|
36 | from IPython.utils.traitlets import ( | |
35 | Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List, |
|
37 | Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List, | |
36 | ) |
|
38 | ) | |
|
39 | from IPython.utils.warn import warn | |||
37 | from IPython.utils.py3compat import ( |
|
40 | from IPython.utils.py3compat import ( | |
38 | unicode_to_str, with_metaclass, PY3, string_types, |
|
41 | unicode_to_str, with_metaclass, PY3, string_types, | |
39 | ) |
|
42 | ) | |
@@ -180,6 +183,16 b' class DisplayFormatter(Configurable):' | |||||
180 | #----------------------------------------------------------------------------- |
|
183 | #----------------------------------------------------------------------------- | |
181 |
|
184 | |||
182 |
|
185 | |||
|
186 | @decorator | |||
|
187 | def warn_format_error(method, self, *args, **kwargs): | |||
|
188 | """decorator for warning on failed format call""" | |||
|
189 | try: | |||
|
190 | return method(self, *args, **kwargs) | |||
|
191 | except Exception as e: | |||
|
192 | warn("Exception in %s formatter: %s" % (self.format_type, e)) | |||
|
193 | return None | |||
|
194 | ||||
|
195 | ||||
183 | class FormatterABC(with_metaclass(abc.ABCMeta, object)): |
|
196 | class FormatterABC(with_metaclass(abc.ABCMeta, object)): | |
184 | """ Abstract base class for Formatters. |
|
197 | """ Abstract base class for Formatters. | |
185 |
|
198 | |||
@@ -194,17 +207,16 b' class FormatterABC(with_metaclass(abc.ABCMeta, object)):' | |||||
194 |
|
207 | |||
195 | # Is the formatter enabled... |
|
208 | # Is the formatter enabled... | |
196 | enabled = True |
|
209 | enabled = True | |
197 |
|
210 | |||
198 | @abc.abstractmethod |
|
211 | @abc.abstractmethod | |
|
212 | @warn_format_error | |||
199 | def __call__(self, obj): |
|
213 | def __call__(self, obj): | |
200 | """Return a JSON'able representation of the object. |
|
214 | """Return a JSON'able representation of the object. | |
201 |
|
215 | |||
202 |
If the object cannot be formatted by this formatter, |
|
216 | If the object cannot be formatted by this formatter, | |
|
217 | warn and return None. | |||
203 | """ |
|
218 | """ | |
204 | try: |
|
219 | return repr(obj) | |
205 | return repr(obj) |
|
|||
206 | except Exception: |
|
|||
207 | return None |
|
|||
208 |
|
220 | |||
209 |
|
221 | |||
210 | def _mod_name_key(typ): |
|
222 | def _mod_name_key(typ): | |
@@ -223,6 +235,7 b' def _get_type(obj):' | |||||
223 |
|
235 | |||
224 | _raise_key_error = object() |
|
236 | _raise_key_error = object() | |
225 |
|
237 | |||
|
238 | ||||
226 | class BaseFormatter(Configurable): |
|
239 | class BaseFormatter(Configurable): | |
227 | """A base formatter class that is configurable. |
|
240 | """A base formatter class that is configurable. | |
228 |
|
241 | |||
@@ -266,24 +279,22 b' class BaseFormatter(Configurable):' | |||||
266 | # Map (modulename, classname) pairs to the format functions. |
|
279 | # Map (modulename, classname) pairs to the format functions. | |
267 | deferred_printers = Dict(config=True) |
|
280 | deferred_printers = Dict(config=True) | |
268 |
|
281 | |||
|
282 | @warn_format_error | |||
269 | def __call__(self, obj): |
|
283 | def __call__(self, obj): | |
270 | """Compute the format for an object.""" |
|
284 | """Compute the format for an object.""" | |
271 | if self.enabled: |
|
285 | if self.enabled: | |
|
286 | # lookup registered printer | |||
272 | try: |
|
287 | try: | |
273 | # lookup registered printer |
|
288 | printer = self.lookup(obj) | |
274 |
|
|
289 | except KeyError: | |
275 | printer = self.lookup(obj) |
|
|||
276 | except KeyError: |
|
|||
277 | pass |
|
|||
278 | else: |
|
|||
279 | return printer(obj) |
|
|||
280 | # Finally look for special method names |
|
|||
281 | method = pretty._safe_getattr(obj, self.print_method, None) |
|
|||
282 | if method is not None: |
|
|||
283 | return method() |
|
|||
284 | return None |
|
|||
285 | except Exception: |
|
|||
286 | pass |
|
290 | pass | |
|
291 | else: | |||
|
292 | return printer(obj) | |||
|
293 | # Finally look for special method names | |||
|
294 | method = pretty._safe_getattr(obj, self.print_method, None) | |||
|
295 | if method is not None: | |||
|
296 | return method() | |||
|
297 | return None | |||
287 | else: |
|
298 | else: | |
288 | return None |
|
299 | return None | |
289 |
|
300 | |||
@@ -599,6 +610,7 b' class PlainTextFormatter(BaseFormatter):' | |||||
599 |
|
610 | |||
600 | #### FormatterABC interface #### |
|
611 | #### FormatterABC interface #### | |
601 |
|
612 | |||
|
613 | @warn_format_error | |||
602 | def __call__(self, obj): |
|
614 | def __call__(self, obj): | |
603 | """Compute the pretty representation of the object.""" |
|
615 | """Compute the pretty representation of the object.""" | |
604 | if not self.pprint: |
|
616 | if not self.pprint: |
@@ -1,5 +1,4 b'' | |||||
1 | """Tests for the Formatters. |
|
1 | """Tests for the Formatters.""" | |
2 | """ |
|
|||
3 |
|
2 | |||
4 | from math import pi |
|
3 | from math import pi | |
5 |
|
4 | |||
@@ -9,7 +8,8 b' except:' | |||||
9 | numpy = None |
|
8 | numpy = None | |
10 | import nose.tools as nt |
|
9 | import nose.tools as nt | |
11 |
|
10 | |||
12 | from IPython.core.formatters import PlainTextFormatter, _mod_name_key |
|
11 | from IPython.core.formatters import PlainTextFormatter, HTMLFormatter, _mod_name_key | |
|
12 | from IPython.utils.io import capture_output | |||
13 |
|
13 | |||
14 | class A(object): |
|
14 | class A(object): | |
15 | def __repr__(self): |
|
15 | def __repr__(self): | |
@@ -231,4 +231,40 b' def test_pop_string():' | |||||
231 | nt.assert_is(f.pop(type_str, None), None) |
|
231 | nt.assert_is(f.pop(type_str, None), None) | |
232 |
|
232 | |||
233 |
|
233 | |||
|
234 | def test_warn_error_method(): | |||
|
235 | f = HTMLFormatter() | |||
|
236 | class BadHTML(object): | |||
|
237 | def _repr_html_(self): | |||
|
238 | return 1/0 | |||
|
239 | bad = BadHTML() | |||
|
240 | with capture_output() as captured: | |||
|
241 | result = f(bad) | |||
|
242 | nt.assert_is(result, None) | |||
|
243 | nt.assert_in("WARNING", captured.stderr) | |||
|
244 | nt.assert_in("text/html", captured.stderr) | |||
|
245 | nt.assert_in("zero", captured.stderr) | |||
|
246 | ||||
|
247 | def test_warn_error_for_type(): | |||
|
248 | f = HTMLFormatter() | |||
|
249 | f.for_type(int, lambda i: name_error) | |||
|
250 | with capture_output() as captured: | |||
|
251 | result = f(5) | |||
|
252 | nt.assert_is(result, None) | |||
|
253 | nt.assert_in("WARNING", captured.stderr) | |||
|
254 | nt.assert_in("text/html", captured.stderr) | |||
|
255 | nt.assert_in("name_error", captured.stderr) | |||
|
256 | ||||
|
257 | def test_warn_error_pretty_method(): | |||
|
258 | f = PlainTextFormatter() | |||
|
259 | class BadPretty(object): | |||
|
260 | def _repr_pretty_(self): | |||
|
261 | return "hello" | |||
|
262 | bad = BadPretty() | |||
|
263 | with capture_output() as captured: | |||
|
264 | result = f(bad) | |||
|
265 | nt.assert_is(result, None) | |||
|
266 | nt.assert_in("WARNING", captured.stderr) | |||
|
267 | nt.assert_in("text/plain", captured.stderr) | |||
|
268 | nt.assert_in("argument", captured.stderr) | |||
|
269 | ||||
234 |
|
270 |
General Comments 0
You need to be logged in to leave comments.
Login now