Show More
@@ -1,27 +1,16 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | """Top-level display functions for displaying object in different formats. | |
|
2 | """Top-level display functions for displaying object in different formats.""" | |
|
3 | 3 | |
|
4 | Authors: | |
|
5 | ||
|
6 | * Brian Granger | |
|
7 | """ | |
|
8 | ||
|
9 | #----------------------------------------------------------------------------- | |
|
10 | # Copyright (C) 2013 The IPython Development Team | |
|
11 | # | |
|
12 | # Distributed under the terms of the BSD License. The full license is in | |
|
13 | # the file COPYING, distributed as part of this software. | |
|
14 | #----------------------------------------------------------------------------- | |
|
15 | ||
|
16 | #----------------------------------------------------------------------------- | |
|
17 | # Imports | |
|
18 | #----------------------------------------------------------------------------- | |
|
4 | # Copyright (c) IPython Development Team. | |
|
5 | # Distributed under the terms of the Modified BSD License. | |
|
19 | 6 | |
|
20 | 7 | from __future__ import print_function |
|
21 | 8 | |
|
9 | import json | |
|
10 | import mimetypes | |
|
22 | 11 | import os |
|
23 | 12 | import struct |
|
24 | import mimetypes | |
|
13 | import warnings | |
|
25 | 14 | |
|
26 | 15 | from IPython.core.formatters import _safe_get_formatter_method |
|
27 | 16 | from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode, |
@@ -514,7 +503,29 b' class SVG(DisplayObject):' | |||
|
514 | 503 | return self.data |
|
515 | 504 | |
|
516 | 505 | |
|
517 |
class JSON( |
|
|
506 | class JSON(DisplayObject): | |
|
507 | """JSON expects a JSON-able dict or list | |
|
508 | ||
|
509 | not an already-serialized JSON string. | |
|
510 | ||
|
511 | Scalar types (None, number, string) are not allowed, only dict or list containers. | |
|
512 | """ | |
|
513 | # wrap data in a property, which warns about passing already-serialized JSON | |
|
514 | _data = None | |
|
515 | def _check_data(self): | |
|
516 | if self.data is not None and not isinstance(self.data, (dict, list)): | |
|
517 | raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data)) | |
|
518 | ||
|
519 | @property | |
|
520 | def data(self): | |
|
521 | return self._data | |
|
522 | ||
|
523 | @data.setter | |
|
524 | def data(self, data): | |
|
525 | if isinstance(data, string_types): | |
|
526 | warnings.warn("JSON expects JSONable dict or list, not JSON strings") | |
|
527 | data = json.loads(data) | |
|
528 | self._data = data | |
|
518 | 529 | |
|
519 | 530 | def _repr_json_(self): |
|
520 | 531 | return self.data |
@@ -12,6 +12,7 b' Inheritance diagram:' | |||
|
12 | 12 | |
|
13 | 13 | import abc |
|
14 | 14 | import inspect |
|
15 | import json | |
|
15 | 16 | import sys |
|
16 | 17 | import traceback |
|
17 | 18 | import warnings |
@@ -232,15 +233,7 b' def warn_format_error(method, self, *args, **kwargs):' | |||
|
232 | 233 | else: |
|
233 | 234 | traceback.print_exception(*exc_info) |
|
234 | 235 | return None |
|
235 | if r is None or isinstance(r, self._return_type) or \ | |
|
236 | (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)): | |
|
237 | return r | |
|
238 | else: | |
|
239 | warnings.warn( | |
|
240 | "%s formatter returned invalid type %s (expected %s) for object: %s" % \ | |
|
241 | (self.format_type, type(r), self._return_type, _safe_repr(args[0])), | |
|
242 | FormatterWarning | |
|
243 | ) | |
|
236 | return self._check_return(r, args[0]) | |
|
244 | 237 | |
|
245 | 238 | |
|
246 | 239 | class FormatterABC(with_metaclass(abc.ABCMeta, object)): |
@@ -259,7 +252,6 b' class FormatterABC(with_metaclass(abc.ABCMeta, object)):' | |||
|
259 | 252 | enabled = True |
|
260 | 253 | |
|
261 | 254 | @abc.abstractmethod |
|
262 | @warn_format_error | |
|
263 | 255 | def __call__(self, obj): |
|
264 | 256 | """Return a JSON'able representation of the object. |
|
265 | 257 | |
@@ -358,6 +350,21 b' class BaseFormatter(Configurable):' | |||
|
358 | 350 | else: |
|
359 | 351 | return True |
|
360 | 352 | |
|
353 | def _check_return(self, r, obj): | |
|
354 | """Check that a return value is appropriate | |
|
355 | ||
|
356 | Return the value if so, None otherwise, warning if invalid. | |
|
357 | """ | |
|
358 | if r is None or isinstance(r, self._return_type) or \ | |
|
359 | (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)): | |
|
360 | return r | |
|
361 | else: | |
|
362 | warnings.warn( | |
|
363 | "%s formatter returned invalid type %s (expected %s) for object: %s" % \ | |
|
364 | (self.format_type, type(r), self._return_type, _safe_repr(obj)), | |
|
365 | FormatterWarning | |
|
366 | ) | |
|
367 | ||
|
361 | 368 | def lookup(self, obj): |
|
362 | 369 | """Look up the formatter for a given instance. |
|
363 | 370 | |
@@ -794,17 +801,42 b' class LatexFormatter(BaseFormatter):' | |||
|
794 | 801 | class JSONFormatter(BaseFormatter): |
|
795 | 802 | """A JSON string formatter. |
|
796 | 803 | |
|
797 |
To define the callables that compute the JSON |
|
|
804 | To define the callables that compute the JSONable representation of | |
|
798 | 805 | your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type` |
|
799 | 806 | or :meth:`for_type_by_name` methods to register functions that handle |
|
800 | 807 | this. |
|
801 | 808 | |
|
802 |
The return value of this formatter should be a |
|
|
809 | The return value of this formatter should be a JSONable list or dict. | |
|
810 | JSON scalars (None, number, string) are not allowed, only dict or list containers. | |
|
803 | 811 | """ |
|
804 | 812 | format_type = Unicode('application/json') |
|
813 | _return_type = (list, dict) | |
|
805 | 814 | |
|
806 | 815 | print_method = ObjectName('_repr_json_') |
|
807 | 816 | |
|
817 | def _check_return(self, r, obj): | |
|
818 | """Check that a return value is appropriate | |
|
819 | ||
|
820 | Return the value if so, None otherwise, warning if invalid. | |
|
821 | """ | |
|
822 | if r is None: | |
|
823 | return | |
|
824 | md = None | |
|
825 | if isinstance(r, tuple): | |
|
826 | # unpack data, metadata tuple for type checking on first element | |
|
827 | r, md = r | |
|
828 | ||
|
829 | # handle deprecated JSON-as-string form from IPython < 3 | |
|
830 | if isinstance(r, string_types): | |
|
831 | warnings.warn("JSON expects JSONable list/dict containers, not JSON strings", | |
|
832 | FormatterWarning) | |
|
833 | r = json.loads(r) | |
|
834 | ||
|
835 | if md is not None: | |
|
836 | # put the tuple back together | |
|
837 | r = (r, md) | |
|
838 | return super(JSONFormatter, self)._check_return(r, obj) | |
|
839 | ||
|
808 | 840 | |
|
809 | 841 | class JavascriptFormatter(BaseFormatter): |
|
810 | 842 | """A Javascript formatter. |
@@ -64,7 +64,7 b' class MagicsDisplay(object):' | |||
|
64 | 64 | return magic_dict |
|
65 | 65 | |
|
66 | 66 | def _repr_json_(self): |
|
67 |
return |
|
|
67 | return self._jsonable() | |
|
68 | 68 | |
|
69 | 69 | |
|
70 | 70 | @magics_class |
@@ -1,11 +1,9 b'' | |||
|
1 | #----------------------------------------------------------------------------- | |
|
2 | # Copyright (C) 2010-2011 The IPython Development Team. | |
|
3 | # | |
|
4 | # Distributed under the terms of the BSD License. | |
|
5 | # | |
|
6 | # The full license is in the file COPYING.txt, distributed with this software. | |
|
7 | #----------------------------------------------------------------------------- | |
|
1 | # Copyright (c) IPython Development Team. | |
|
2 | # Distributed under the terms of the Modified BSD License. | |
|
3 | ||
|
4 | import json | |
|
8 | 5 | import os |
|
6 | import warnings | |
|
9 | 7 | |
|
10 | 8 | import nose.tools as nt |
|
11 | 9 | |
@@ -130,3 +128,21 b' def test_displayobject_repr():' | |||
|
130 | 128 | nt.assert_equal(repr(j), object.__repr__(j)) |
|
131 | 129 | j._show_mem_addr = False |
|
132 | 130 | nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>') |
|
131 | ||
|
132 | def test_json(): | |
|
133 | d = {'a': 5} | |
|
134 | lis = [d] | |
|
135 | j = display.JSON(d) | |
|
136 | nt.assert_equal(j._repr_json_(), d) | |
|
137 | with warnings.catch_warnings(record=True) as w: | |
|
138 | j = display.JSON(json.dumps(d)) | |
|
139 | assert len(w) == 1 | |
|
140 | nt.assert_equal(j._repr_json_(), d) | |
|
141 | j = display.JSON(lis) | |
|
142 | nt.assert_equal(j._repr_json_(), lis) | |
|
143 | with warnings.catch_warnings(record=True) as w: | |
|
144 | j = display.JSON(json.dumps(lis)) | |
|
145 | assert len(w) == 1 | |
|
146 | nt.assert_equal(j._repr_json_(), lis) | |
|
147 | ||
|
148 | No newline at end of file |
@@ -1,5 +1,6 b'' | |||
|
1 | 1 | """Tests for the Formatters.""" |
|
2 | 2 | |
|
3 | import warnings | |
|
3 | 4 | from math import pi |
|
4 | 5 | |
|
5 | 6 | try: |
@@ -8,10 +9,11 b' except:' | |||
|
8 | 9 | numpy = None |
|
9 | 10 | import nose.tools as nt |
|
10 | 11 | |
|
12 | from IPython import get_ipython | |
|
11 | 13 | from IPython.config import Config |
|
12 | 14 | from IPython.core.formatters import ( |
|
13 | 15 | PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key, |
|
14 | DisplayFormatter, | |
|
16 | DisplayFormatter, JSONFormatter, | |
|
15 | 17 | ) |
|
16 | 18 | from IPython.utils.io import capture_output |
|
17 | 19 | |
@@ -418,3 +420,14 b' def test_ipython_display_formatter():' | |||
|
418 | 420 | nt.assert_equal(md, {}) |
|
419 | 421 | nt.assert_equal(catcher, [yes]) |
|
420 | 422 | |
|
423 | def test_json_as_string_deprecated(): | |
|
424 | class JSONString(object): | |
|
425 | def _repr_json_(self): | |
|
426 | return '{}' | |
|
427 | ||
|
428 | f = JSONFormatter() | |
|
429 | with warnings.catch_warnings(record=True) as w: | |
|
430 | d = f(JSONString()) | |
|
431 | nt.assert_equal(d, {}) | |
|
432 | nt.assert_equal(len(w), 1) | |
|
433 | No newline at end of file |
@@ -8,6 +8,7 b' from __future__ import absolute_import' | |||
|
8 | 8 | import io |
|
9 | 9 | import os |
|
10 | 10 | import sys |
|
11 | import warnings | |
|
11 | 12 | from unittest import TestCase, skipIf |
|
12 | 13 | |
|
13 | 14 | try: |
@@ -18,6 +19,7 b' except ImportError:' | |||
|
18 | 19 | |
|
19 | 20 | import nose.tools as nt |
|
20 | 21 | |
|
22 | from IPython import get_ipython | |
|
21 | 23 | from IPython.core import magic |
|
22 | 24 | from IPython.core.error import UsageError |
|
23 | 25 | from IPython.core.magic import (Magics, magics_class, line_magic, |
@@ -980,3 +982,13 b' def test_bookmark():' | |||
|
980 | 982 | with tt.AssertPrints('bmname'): |
|
981 | 983 | ip.run_line_magic('bookmark', '-l') |
|
982 | 984 | ip.run_line_magic('bookmark', '-d bmname') |
|
985 | ||
|
986 | def test_ls_magic(): | |
|
987 | ip = get_ipython() | |
|
988 | json_formatter = ip.display_formatter.formatters['application/json'] | |
|
989 | json_formatter.enabled = True | |
|
990 | lsmagic = ip.magic('lsmagic') | |
|
991 | with warnings.catch_warnings(record=True) as w: | |
|
992 | j = json_formatter(lsmagic) | |
|
993 | nt.assert_equal(sorted(j), ['cell', 'line']) | |
|
994 | nt.assert_equal(w, []) # no warnings |
General Comments 0
You need to be logged in to leave comments.
Login now