diff --git a/ipykernel/datapub.py b/ipykernel/datapub.py index c124195cb..fbc263d08 100644 --- a/ipykernel/datapub.py +++ b/ipykernel/datapub.py @@ -66,6 +66,6 @@ def publish_data(data): DeprecationWarning, stacklevel=2 ) - + from ipykernel.zmqshell import ZMQInteractiveShell ZMQInteractiveShell.instance().data_pub.publish_data(data) diff --git a/ipykernel/debugger.py b/ipykernel/debugger.py index aa4ea8beb..0f514aba7 100644 --- a/ipykernel/debugger.py +++ b/ipykernel/debugger.py @@ -7,10 +7,15 @@ from tornado.queues import Queue from tornado.locks import Event -from .compiler import (get_file_name, get_tmp_directory, get_tmp_hash_seed) - from IPython.core.getipython import get_ipython +try: + from jupyter_client.jsonutil import json_default +except ImportError: + from jupyter_client.jsonutil import date_default as json_default + +from .compiler import (get_file_name, get_tmp_directory, get_tmp_hash_seed) + # This import is required to have the next ones working... from debugpy.server import api # noqa from _pydevd_bundle import pydevd_frame_utils @@ -170,7 +175,12 @@ def _forward_event(self, msg): def _send_request(self, msg): if self.routing_id is None: self.routing_id = self.debugpy_stream.socket.getsockopt(ROUTING_ID) - content = jsonapi.dumps(msg) + content = jsonapi.dumps( + msg, + default=json_default, + ensure_ascii=False, + allow_nan=False, + ) content_length = str(len(content)) buf = (DebugpyMessageQueue.HEADER + content_length + DebugpyMessageQueue.SEPARATOR).encode('ascii') buf += content @@ -240,6 +250,7 @@ async def send_dap_request(self, msg): self.log.debug(rep) return rep + class Debugger: # Requests that requires that the debugger has started diff --git a/ipykernel/inprocess/ipkernel.py b/ipykernel/inprocess/ipkernel.py index 9a10117eb..bbe2b4345 100644 --- a/ipykernel/inprocess/ipkernel.py +++ b/ipykernel/inprocess/ipkernel.py @@ -152,12 +152,12 @@ def _default_shell_class(self): @default('stdout') def _default_stdout(self): - return OutStream(self.session, self.iopub_thread, 'stdout', + return OutStream(self.session, self.iopub_thread, 'stdout', watchfd=False) @default('stderr') def _default_stderr(self): - return OutStream(self.session, self.iopub_thread, 'stderr', + return OutStream(self.session, self.iopub_thread, 'stderr', watchfd=False) #----------------------------------------------------------------------------- diff --git a/ipykernel/jsonutil.py b/ipykernel/jsonutil.py index 514955715..a777cc72a 100644 --- a/ipykernel/jsonutil.py +++ b/ipykernel/jsonutil.py @@ -9,6 +9,7 @@ import types from datetime import datetime import numbers +from jupyter_client._version import version_info as jupyter_client_version next_attr_name = '__next__' @@ -42,6 +43,9 @@ # front of PDF base64-encoded PDF64 = b'JVBER' +JUPYTER_CLIENT_MAJOR_VERSION = jupyter_client_version[0] + + def encode_images(format_dict): """b64-encodes images in a displaypub format dict @@ -68,7 +72,9 @@ def encode_images(format_dict): def json_clean(obj): - """Clean an object to ensure it's safe to encode in JSON. + """Deprecated, this is a no-op for jupyter-client>=7. + + Clean an object to ensure it's safe to encode in JSON. Atomic, immutable objects are returned unmodified. Sets and tuples are converted to lists, lists are copied and dicts are also copied. @@ -89,6 +95,9 @@ def json_clean(obj): it simply sanitizes it so that there will be no encoding errors later. """ + if JUPYTER_CLIENT_MAJOR_VERSION >= 7: + return obj + # types that are 'atomic' and ok in json as-is. atomic_ok = (str, type(None)) @@ -110,10 +119,10 @@ def json_clean(obj): if math.isnan(obj) or math.isinf(obj): return repr(obj) return float(obj) - + if isinstance(obj, atomic_ok): return obj - + if isinstance(obj, bytes): # unanmbiguous binary data is base64-encoded # (this probably should have happened upstream) @@ -142,6 +151,6 @@ def json_clean(obj): return out if isinstance(obj, datetime): return obj.strftime(ISO8601) - + # we don't understand it, it's probably an unserializable object raise ValueError("Can't clean for JSON: %r" % obj) diff --git a/ipykernel/tests/test_jsonutil.py b/ipykernel/tests/test_jsonutil.py index 9e9f0e5e3..511cad39e 100644 --- a/ipykernel/tests/test_jsonutil.py +++ b/ipykernel/tests/test_jsonutil.py @@ -11,20 +11,28 @@ import pytest +from jupyter_client._version import version_info as jupyter_client_version + from .. import jsonutil from ..jsonutil import json_clean, encode_images + +JUPYTER_CLIENT_MAJOR_VERSION = jupyter_client_version[0] + + class MyInt(object): def __int__(self): return 389 numbers.Integral.register(MyInt) + class MyFloat(object): def __float__(self): return 3.14 numbers.Real.register(MyFloat) +@pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test(): # list of input/expected output. Use None for the expected output if it # can be the same as the input. @@ -47,7 +55,7 @@ def test(): (MyFloat(), 3.14), (MyInt(), 389) ] - + for val, jval in pairs: if jval is None: jval = val @@ -58,13 +66,14 @@ def test(): json.loads(json.dumps(out)) +@pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_encode_images(): # invalid data, but the header and footer are from real files pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82' jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9' pdfdata = b'%PDF-1.\ntrailer<>]>>>>>>' bindata = b'\xff\xff\xff\xff' - + fmt = { 'image/png' : pngdata, 'image/jpeg' : jpegdata, @@ -78,16 +87,18 @@ def test_encode_images(): assert decoded == value encoded2 = json_clean(encode_images(encoded)) assert encoded == encoded2 - + for key, value in fmt.items(): decoded = a2b_base64(encoded[key]) assert decoded == value +@pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_lambda(): with pytest.raises(ValueError): json_clean(lambda : 1) +@pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_exception(): bad_dicts = [{1:'number', '1':'string'}, {True:'bool', 'True':'string'}, @@ -97,6 +108,7 @@ def test_exception(): json_clean(d) +@pytest.mark.skipif(JUPYTER_CLIENT_MAJOR_VERSION >= 7, reason="json_clean is a no-op") def test_unicode_dict(): data = {'üniço∂e': 'üniço∂e'} clean = jsonutil.json_clean(data)