##// END OF EJS Templates
JSON formatter expects JSONable dict/list...
Min RK -
Show More
@@ -1,27 +1,16 b''
1 # -*- coding: utf-8 -*-
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:
4 # Copyright (c) IPython Development Team.
5
5 # Distributed under the terms of the Modified BSD License.
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 #-----------------------------------------------------------------------------
19
6
20 from __future__ import print_function
7 from __future__ import print_function
21
8
9 import json
10 import mimetypes
22 import os
11 import os
23 import struct
12 import struct
24 import mimetypes
13 import warnings
25
14
26 from IPython.core.formatters import _safe_get_formatter_method
15 from IPython.core.formatters import _safe_get_formatter_method
27 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
16 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
@@ -514,7 +503,29 b' class SVG(DisplayObject):'
514 return self.data
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 def _repr_json_(self):
530 def _repr_json_(self):
520 return self.data
531 return self.data
@@ -12,6 +12,7 b' Inheritance diagram:'
12
12
13 import abc
13 import abc
14 import inspect
14 import inspect
15 import json
15 import sys
16 import sys
16 import traceback
17 import traceback
17 import warnings
18 import warnings
@@ -232,15 +233,7 b' def warn_format_error(method, self, *args, **kwargs):'
232 else:
233 else:
233 traceback.print_exception(*exc_info)
234 traceback.print_exception(*exc_info)
234 return None
235 return None
235 if r is None or isinstance(r, self._return_type) or \
236 return self._check_return(r, args[0])
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 )
244
237
245
238
246 class FormatterABC(with_metaclass(abc.ABCMeta, object)):
239 class FormatterABC(with_metaclass(abc.ABCMeta, object)):
@@ -259,7 +252,6 b' class FormatterABC(with_metaclass(abc.ABCMeta, object)):'
259 enabled = True
252 enabled = True
260
253
261 @abc.abstractmethod
254 @abc.abstractmethod
262 @warn_format_error
263 def __call__(self, obj):
255 def __call__(self, obj):
264 """Return a JSON'able representation of the object.
256 """Return a JSON'able representation of the object.
265
257
@@ -358,6 +350,21 b' class BaseFormatter(Configurable):'
358 else:
350 else:
359 return True
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 def lookup(self, obj):
368 def lookup(self, obj):
362 """Look up the formatter for a given instance.
369 """Look up the formatter for a given instance.
363
370
@@ -794,16 +801,41 b' class LatexFormatter(BaseFormatter):'
794 class JSONFormatter(BaseFormatter):
801 class JSONFormatter(BaseFormatter):
795 """A JSON string formatter.
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 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
805 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
799 or :meth:`for_type_by_name` methods to register functions that handle
806 or :meth:`for_type_by_name` methods to register functions that handle
800 this.
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 format_type = Unicode('application/json')
812 format_type = Unicode('application/json')
813 _return_type = (list, dict)
805
814
806 print_method = ObjectName('_repr_json_')
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 class JavascriptFormatter(BaseFormatter):
841 class JavascriptFormatter(BaseFormatter):
@@ -64,7 +64,7 b' class MagicsDisplay(object):'
64 return magic_dict
64 return magic_dict
65
65
66 def _repr_json_(self):
66 def _repr_json_(self):
67 return json.dumps(self._jsonable())
67 return self._jsonable()
68
68
69
69
70 @magics_class
70 @magics_class
@@ -1,11 +1,9 b''
1 #-----------------------------------------------------------------------------
1 # Copyright (c) IPython Development Team.
2 # Copyright (C) 2010-2011 The IPython Development Team.
2 # Distributed under the terms of the Modified BSD License.
3 #
3
4 # Distributed under the terms of the BSD License.
4 import json
5 #
6 # The full license is in the file COPYING.txt, distributed with this software.
7 #-----------------------------------------------------------------------------
8 import os
5 import os
6 import warnings
9
7
10 import nose.tools as nt
8 import nose.tools as nt
11
9
@@ -130,3 +128,21 b' def test_displayobject_repr():'
130 nt.assert_equal(repr(j), object.__repr__(j))
128 nt.assert_equal(repr(j), object.__repr__(j))
131 j._show_mem_addr = False
129 j._show_mem_addr = False
132 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
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 """Tests for the Formatters."""
1 """Tests for the Formatters."""
2
2
3 import warnings
3 from math import pi
4 from math import pi
4
5
5 try:
6 try:
@@ -8,10 +9,11 b' except:'
8 numpy = None
9 numpy = None
9 import nose.tools as nt
10 import nose.tools as nt
10
11
12 from IPython import get_ipython
11 from IPython.config import Config
13 from IPython.config import Config
12 from IPython.core.formatters import (
14 from IPython.core.formatters import (
13 PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key,
15 PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key,
14 DisplayFormatter,
16 DisplayFormatter, JSONFormatter,
15 )
17 )
16 from IPython.utils.io import capture_output
18 from IPython.utils.io import capture_output
17
19
@@ -418,3 +420,14 b' def test_ipython_display_formatter():'
418 nt.assert_equal(md, {})
420 nt.assert_equal(md, {})
419 nt.assert_equal(catcher, [yes])
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 import io
8 import io
9 import os
9 import os
10 import sys
10 import sys
11 import warnings
11 from unittest import TestCase, skipIf
12 from unittest import TestCase, skipIf
12
13
13 try:
14 try:
@@ -18,6 +19,7 b' except ImportError:'
18
19
19 import nose.tools as nt
20 import nose.tools as nt
20
21
22 from IPython import get_ipython
21 from IPython.core import magic
23 from IPython.core import magic
22 from IPython.core.error import UsageError
24 from IPython.core.error import UsageError
23 from IPython.core.magic import (Magics, magics_class, line_magic,
25 from IPython.core.magic import (Magics, magics_class, line_magic,
@@ -980,3 +982,13 b' def test_bookmark():'
980 with tt.AssertPrints('bmname'):
982 with tt.AssertPrints('bmname'):
981 ip.run_line_magic('bookmark', '-l')
983 ip.run_line_magic('bookmark', '-l')
982 ip.run_line_magic('bookmark', '-d bmname')
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