##// END OF EJS Templates
JSON formatter expects JSONable dict/list...
Min RK -
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(TextDisplayObject):
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,16 +801,41 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 string representation of
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 valid JSON string.
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_')
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)
807 839
808 840
809 841 class JavascriptFormatter(BaseFormatter):
@@ -64,7 +64,7 b' class MagicsDisplay(object):'
64 64 return magic_dict
65 65
66 66 def _repr_json_(self):
67 return json.dumps(self._jsonable())
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