[Python-modules-commits] [ipykernel] 01/09: Import ipykernel_4.7.0.orig.tar.gz

Gordon Ball chronitis-guest at moszumanska.debian.org
Wed Dec 13 08:50:55 UTC 2017


This is an automated email from the git hooks/post-receive script.

chronitis-guest pushed a commit to branch master
in repository ipykernel.

commit 017d6a026d10428f285efa959cfdd02d07c6d4ac
Author: Gordon Ball <gordon at chronitis.net>
Date:   Wed Dec 13 08:29:13 2017 +0000

    Import ipykernel_4.7.0.orig.tar.gz
---
 .travis.yml                                     |  5 +-
 appveyor.yml                                    |  6 +-
 docs/changelog.rst                              | 18 ++++++
 docs/conf.py                                    |  2 +-
 ipykernel/_version.py                           |  2 +-
 ipykernel/connect.py                            |  9 ++-
 ipykernel/displayhook.py                        |  4 +-
 ipykernel/eventloops.py                         | 29 +++++++++
 ipykernel/inprocess/tests/test_kernel.py        |  6 +-
 ipykernel/inprocess/tests/test_kernelmanager.py | 28 ++++-----
 ipykernel/iostream.py                           | 70 ++++++++++++++-------
 ipykernel/ipkernel.py                           | 83 ++++++++++++++++++++-----
 ipykernel/jsonutil.py                           | 46 ++++++++++----
 ipykernel/kernelapp.py                          |  3 +-
 ipykernel/kernelbase.py                         | 11 ++--
 ipykernel/pylab/backend_inline.py               |  6 +-
 ipykernel/tests/_asyncio.py                     | 17 +++++
 ipykernel/tests/test_connect.py                 | 18 +++---
 ipykernel/tests/test_embed_kernel.py            | 27 ++++----
 ipykernel/tests/test_eventloop.py               | 44 +++++++++++++
 ipykernel/tests/test_jsonutil.py                | 43 ++++++-------
 ipykernel/tests/test_kernel.py                  | 63 ++++++++++---------
 ipykernel/tests/test_kernelspec.py              | 18 +++---
 ipykernel/tests/test_message_spec.py            | 52 ++++++++--------
 ipykernel/tests/test_pickleutil.py              | 13 ++--
 ipykernel/tests/test_serialize.py               | 83 +++++++++++--------------
 ipykernel/tests/test_start_kernel.py            | 12 ++--
 ipykernel/tests/test_zmq_shell.py               | 43 +++++++------
 ipykernel/tests/utils.py                        | 21 ++++---
 ipykernel/zmqshell.py                           |  4 +-
 setup.cfg                                       |  9 +--
 setup.py                                        |  8 ++-
 32 files changed, 501 insertions(+), 302 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 32869e4..c68a67e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
 language: python
 python:
     - "nightly"
-    - 3.6 
+    - 3.6
     - 3.5
     - 3.4
     - 3.3
@@ -16,9 +16,10 @@ install:
       if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ||  "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
         pip install matplotlib
       fi
+    - pip freeze
 script:
     - jupyter kernelspec list
-    - nosetests --with-coverage --with-timer --cover-package ipykernel ipykernel
+    - pytest --cov ipykernel --durations 10 -v ipykernel
 after_success:
     - codecov
 matrix:
diff --git a/appveyor.yml b/appveyor.yml
index 939ea8f..fe9d253 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -21,14 +21,14 @@ install:
         pip install --upgrade pip wheel
         pip --version
   - cmd: |
-        pip install --pre -e . coverage nose_warnings_filters
-        pip install ipykernel[test] nose-timer
+        pip install --pre -e .
+        pip install ipykernel[test]
   - cmd: |
         pip install matplotlib numpy
         pip freeze
   - cmd: python -c "import ipykernel.kernelspec; ipykernel.kernelspec.install(user=True)"
 test_script:
-  - cmd: nosetests --with-coverage --with-timer --cover-package=ipykernel ipykernel
+  - cmd: pytest -v --cov ipykernel ipykernel
 
 on_success:
   - cmd: pip install codecov
diff --git a/docs/changelog.rst b/docs/changelog.rst
index b49d4dc..a5d950d 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,24 @@
 Changes in IPython kernel
 =========================
 
+4.7
+---
+
+4.7.0
+*****
+
+`4.7.0 on GitHub <https://github.com/ipython/ipykernel/milestones/4.7>`__
+
+- Add event loop integration for :mod:`asyncio`.
+- Use the new IPython completer API.
+- Add support for displaying GIF images (mimetype ``image/gif``).
+- Allow the kernel to be interrupted without killing the Qt console.
+- Fix ``is_complete`` response with cell magics.
+- Clean up encoding of bytes objects.
+- Clean up help links to use ``https`` and improve display titles.
+- Clean up ioloop handling in preparation for tornado 5.
+
+
 4.6
 ---
 
diff --git a/docs/conf.py b/docs/conf.py
index d44ecd4..4c03611 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -297,7 +297,7 @@ texinfo_documents = [
 
 # Example configuration for intersphinx: refer to the Python standard library.
 intersphinx_mapping = {
-    'https://docs.python.org/': None,
+    'python': ('https://docs.python.org/3/', None),
     'ipython': ('https://ipython.readthedocs.io/en/latest', None),
     'jupyter': ('https://jupyter.readthedocs.io/en/latest', None),
 }
diff --git a/ipykernel/_version.py b/ipykernel/_version.py
index fbcadc4..645a51e 100644
--- a/ipykernel/_version.py
+++ b/ipykernel/_version.py
@@ -1,4 +1,4 @@
-version_info = (4, 6, 1)
+version_info = (4, 7, 0)
 __version__ = '.'.join(map(str, version_info))
 
 kernel_protocol_version_info = (5, 1)
diff --git a/ipykernel/connect.py b/ipykernel/connect.py
index e49ad10..faaa402 100644
--- a/ipykernel/connect.py
+++ b/ipykernel/connect.py
@@ -13,7 +13,7 @@ import warnings
 from IPython.core.profiledir import ProfileDir
 from IPython.paths import get_ipython_dir
 from ipython_genutils.path import filefind
-from ipython_genutils.py3compat import str_to_bytes
+from ipython_genutils.py3compat import str_to_bytes, PY3
 
 import jupyter_client
 from jupyter_client import write_connection_file
@@ -169,8 +169,15 @@ def connect_qtconsole(connection_file=None, argv=None, profile=None):
         "qtconsoleapp.main()"
     ])
 
+    kwargs = {}
+    if PY3:
+        # Launch the Qt console in a separate session & process group, so
+        # interrupting the kernel doesn't kill it. This kwarg is not on Py2.
+        kwargs['start_new_session'] = True
+
     return Popen([sys.executable, '-c', cmd, '--existing', cf] + argv,
         stdout=PIPE, stderr=PIPE, close_fds=(sys.platform != 'win32'),
+        **kwargs
     )
 
 
diff --git a/ipykernel/displayhook.py b/ipykernel/displayhook.py
index 6e263ab..7e90ef1 100644
--- a/ipykernel/displayhook.py
+++ b/ipykernel/displayhook.py
@@ -6,7 +6,7 @@
 import sys
 
 from IPython.core.displayhook import DisplayHook
-from ipykernel.jsonutil import encode_images
+from ipykernel.jsonutil import encode_images, json_clean
 from ipython_genutils.py3compat import builtin_mod
 from traitlets import Instance, Dict, Any
 from jupyter_client.session import extract_header, Session
@@ -68,7 +68,7 @@ class ZMQShellDisplayHook(DisplayHook):
         self.msg['content']['execution_count'] = self.prompt_count
 
     def write_format_data(self, format_dict, md_dict=None):
-        self.msg['content']['data'] = encode_images(format_dict)
+        self.msg['content']['data'] = json_clean(encode_images(format_dict))
         self.msg['content']['metadata'] = md_dict
 
     def finish_displayhook(self):
diff --git a/ipykernel/eventloops.py b/ipykernel/eventloops.py
index d5cc740..86f21cd 100644
--- a/ipykernel/eventloops.py
+++ b/ipykernel/eventloops.py
@@ -48,6 +48,7 @@ loop_map = {
     'nbagg': None,
     'notebook': None,
     'ipympl': None,
+    'widget': None,
     None : None,
 }
 
@@ -290,6 +291,34 @@ def loop_cocoa(kernel):
             sys.excepthook = real_excepthook
 
 
+ at register_integration('asyncio')
+def loop_asyncio(kernel):
+    '''Start a kernel with asyncio event loop support.'''
+    import asyncio
+    loop = asyncio.get_event_loop()
+
+    def kernel_handler():
+        loop.call_soon(kernel.do_one_iteration)
+        loop.call_later(kernel._poll_interval, kernel_handler)
+
+    loop.call_soon(kernel_handler)
+    # loop is already running (e.g. tornado 5), nothing left to do
+    if loop.is_running():
+        return
+    while True:
+        error = None
+        try:
+            loop.run_forever()
+        except KeyboardInterrupt:
+            continue
+        except Exception as e:
+            error = e
+        if hasattr(loop, 'shutdown_asyncgens'):
+            loop.run_until_complete(loop.shutdown_asyncgens())
+        loop.close()
+        if error is not None:
+            raise error
+        break
 
 def enable_gui(gui, kernel=None):
     """Enable integration with a given GUI"""
diff --git a/ipykernel/inprocess/tests/test_kernel.py b/ipykernel/inprocess/tests/test_kernel.py
index 0231c86..aa7cf67 100644
--- a/ipykernel/inprocess/tests/test_kernel.py
+++ b/ipykernel/inprocess/tests/test_kernel.py
@@ -50,7 +50,7 @@ class InProcessKernelTestCase(unittest.TestCase):
                 self.kc.execute('x = raw_input()')
         finally:
             sys.stdin = sys_stdin
-        self.assertEqual(self.km.kernel.shell.user_ns.get('x'), 'foobar')
+        assert self.km.kernel.shell.user_ns.get('x') == 'foobar'
 
     def test_stdout(self):
         """ Does the in-process kernel correctly capture IO?
@@ -59,13 +59,13 @@ class InProcessKernelTestCase(unittest.TestCase):
 
         with capture_output() as io:
             kernel.shell.run_cell('print("foo")')
-        self.assertEqual(io.stdout, 'foo\n')
+        assert io.stdout == 'foo\n'
 
         kc = BlockingInProcessKernelClient(kernel=kernel, session=kernel.session)
         kernel.frontends.append(kc)
         kc.execute('print("bar")')
         out, err = assemble_output(kc.iopub_channel)
-        self.assertEqual(out, 'bar\n')
+        assert out == 'bar\n'
 
     def test_getpass_stream(self):
         "Tests that kernel getpass accept the stream parameter"
diff --git a/ipykernel/inprocess/tests/test_kernelmanager.py b/ipykernel/inprocess/tests/test_kernelmanager.py
index f3e4436..9de5994 100644
--- a/ipykernel/inprocess/tests/test_kernelmanager.py
+++ b/ipykernel/inprocess/tests/test_kernelmanager.py
@@ -25,31 +25,31 @@ class InProcessKernelManagerTestCase(unittest.TestCase):
         """ Does the in-process kernel manager implement the basic KM interface?
         """
         km = self.km
-        self.assert_(not km.has_kernel)
+        assert not km.has_kernel
 
         km.start_kernel()
-        self.assert_(km.has_kernel)
-        self.assert_(km.kernel is not None)
+        assert km.has_kernel
+        assert km.kernel is not None
 
         kc = km.client()
-        self.assert_(not kc.channels_running)
+        assert not kc.channels_running
 
         kc.start_channels()
-        self.assert_(kc.channels_running)
+        assert kc.channels_running
 
         old_kernel = km.kernel
         km.restart_kernel()
         self.assertIsNotNone(km.kernel)
-        self.assertNotEquals(km.kernel, old_kernel)
+        assert km.kernel != old_kernel
 
         km.shutdown_kernel()
-        self.assert_(not km.has_kernel)
+        assert not km.has_kernel
 
         self.assertRaises(NotImplementedError, km.interrupt_kernel)
         self.assertRaises(NotImplementedError, km.signal_kernel, 9)
 
         kc.stop_channels()
-        self.assert_(not kc.channels_running)
+        assert not kc.channels_running
 
     def test_execute(self):
         """ Does executing code in an in-process kernel work?
@@ -60,7 +60,7 @@ class InProcessKernelManagerTestCase(unittest.TestCase):
         kc.start_channels()
         kc.wait_for_ready()
         kc.execute('foo = 1')
-        self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
+        assert km.kernel.shell.user_ns['foo'] == 1
 
     def test_complete(self):
         """ Does requesting completion from an in-process kernel work?
@@ -73,7 +73,7 @@ class InProcessKernelManagerTestCase(unittest.TestCase):
         km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
         kc.complete('my_ba', 5)
         msg = kc.get_shell_msg()
-        self.assertEqual(msg['header']['msg_type'], 'complete_reply')
+        assert msg['header']['msg_type'] == 'complete_reply'
         self.assertEqual(sorted(msg['content']['matches']),
                           ['my_bar', 'my_baz'])
 
@@ -88,7 +88,7 @@ class InProcessKernelManagerTestCase(unittest.TestCase):
         km.kernel.shell.user_ns['foo'] = 1
         kc.inspect('foo')
         msg = kc.get_shell_msg()
-        self.assertEqual(msg['header']['msg_type'], 'inspect_reply')
+        assert msg['header']['msg_type'] == 'inspect_reply'
         content = msg['content']
         assert content['found']
         text = content['data']['text/plain']
@@ -105,10 +105,10 @@ class InProcessKernelManagerTestCase(unittest.TestCase):
         kc.execute('1')
         kc.history(hist_access_type='tail', n=1)
         msg = kc.shell_channel.get_msgs()[-1]
-        self.assertEquals(msg['header']['msg_type'], 'history_reply')
+        assert msg['header']['msg_type'] == 'history_reply'
         history = msg['content']['history']
-        self.assertEquals(len(history), 1)
-        self.assertEquals(history[0][2], '1')
+        assert len(history) == 1
+        assert history[0][2] == '1'
 
 
 if __name__ == '__main__':
diff --git a/ipykernel/iostream.py b/ipykernel/iostream.py
index 4e99e96..0d11d9e 100644
--- a/ipykernel/iostream.py
+++ b/ipykernel/iostream.py
@@ -7,11 +7,16 @@
 from __future__ import print_function
 import atexit
 from binascii import b2a_hex
+from collections import deque
+try:
+    from importlib import lock_held as import_lock_held
+except ImportError:
+    from imp import lock_held as import_lock_held
 import os
 import sys
 import threading
 import warnings
-from io import StringIO, UnsupportedOperation, TextIOBase
+from io import StringIO, TextIOBase
 
 import zmq
 from zmq.eventloop.ioloop import IOLoop
@@ -58,17 +63,18 @@ class IOPubThread(object):
         self.background_socket = BackgroundSocket(self)
         self._master_pid = os.getpid()
         self._pipe_flag = pipe
-        self.io_loop = IOLoop()
+        self.io_loop = IOLoop(make_current=False)
         if pipe:
             self._setup_pipe_in()
         self._local = threading.local()
-        self._events = {}
+        self._events = deque()
         self._setup_event_pipe()
         self.thread = threading.Thread(target=self._thread_main)
         self.thread.daemon = True
 
     def _thread_main(self):
         """The inner loop that's actually run in a thread"""
+        self.io_loop.make_current()
         self.io_loop.start()
         self.io_loop.close(all_fds=True)
 
@@ -83,7 +89,7 @@ class IOPubThread(object):
         pipe_in.bind(iface)
         self._event_puller = ZMQStream(pipe_in, self.io_loop)
         self._event_puller.on_recv(self._handle_event)
-    
+
     @property
     def _event_pipe(self):
         """thread-local event pipe for signaling events that should be processed in the thread"""
@@ -99,11 +105,20 @@ class IOPubThread(object):
         return event_pipe
 
     def _handle_event(self, msg):
-        """Handle an event on the event pipe"""
-        event_id = msg[0]
-        event_f = self._events.pop(event_id)
-        event_f()
-    
+        """Handle an event on the event pipe
+
+        Content of the message is ignored.
+
+        Whenever *an* event arrives on the event stream,
+        *all* waiting events are processed in order.
+        """
+        # freeze event count so new writes don't extend the queue
+        # while we are processing
+        n_events = len(self._events)
+        for i in range(n_events):
+            event_f = self._events.popleft()
+            event_f()
+
     def _setup_pipe_in(self):
         """setup listening pipe for IOPub from forked subprocesses"""
         ctx = self.socket.context
@@ -125,7 +140,7 @@ class IOPubThread(object):
             return
         self._pipe_in = ZMQStream(pipe_in, self.io_loop)
         self._pipe_in.on_recv(self._handle_pipe_msg)
-    
+
     def _handle_pipe_msg(self, msg):
         """handle a pipe message from a subprocess"""
         if not self._pipe_flag or not self._is_master_process():
@@ -183,11 +198,9 @@ class IOPubThread(object):
         If the thread is not running, call immediately.
         """
         if self.thread.is_alive():
-            event_id = os.urandom(16)
-            while event_id in self._events:
-                event_id = os.urandom(16)
-            self._events[event_id] = f
-            self._event_pipe.send(event_id)
+            self._events.append(f)
+            # wake event thread (message content is ignored)
+            self._event_pipe.send(b'')
         else:
             f()
 
@@ -255,6 +268,9 @@ class OutStream(TextIOBase):
     Output is handed off to an IO Thread
     """
 
+    # timeout for flush to avoid infinite hang
+    # in case of misbehavior
+    flush_timeout = 10
     # The time interval between automatic flushes, in seconds.
     flush_interval = 0.2
     topic = None
@@ -296,7 +312,7 @@ class OutStream(TextIOBase):
 
     def _schedule_flush(self):
         """schedule a flush in the IO thread
-        
+
         call this on write, to indicate that flush should be called soon.
         """
         if self._flush_pending:
@@ -310,21 +326,29 @@ class OutStream(TextIOBase):
 
     def flush(self):
         """trigger actual zmq send
-        
+
         send will happen in the background thread
         """
         if self.pub_thread.thread.is_alive():
-            # wait for flush to actually get through:
+            # request flush on the background thread
             self.pub_thread.schedule(self._flush)
-            evt = threading.Event()
-            self.pub_thread.schedule(evt.set)
-            evt.wait()
+            # wait for flush to actually get through, if we can.
+            # waiting across threads during import can cause deadlocks
+            # so only wait if import lock is not held
+            if not import_lock_held():
+                evt = threading.Event()
+                self.pub_thread.schedule(evt.set)
+                # and give a timeout to avoid
+                if not evt.wait(self.flush_timeout):
+                    # write directly to __stderr__ instead of warning because
+                    # if this is happening sys.stderr may be the problem.
+                    print("IOStream.flush timed out", file=sys.__stderr__)
         else:
             self._flush()
-    
+
     def _flush(self):
         """This is where the actual send happens.
-        
+
         _flush should generally be called in the IO thread,
         unless the thread has been destroyed (e.g. forked subprocess).
         """
diff --git a/ipykernel/ipkernel.py b/ipykernel/ipkernel.py
index c9627ad..6304131 100644
--- a/ipykernel/ipkernel.py
+++ b/ipykernel/ipkernel.py
@@ -2,23 +2,35 @@
 
 import getpass
 import sys
-import traceback
 
 from IPython.core import release
 from ipython_genutils.py3compat import builtin_mod, PY3, unicode_type, safe_unicode
 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
-from traitlets import Instance, Type, Any, List
+from traitlets import Instance, Type, Any, List, Bool
 
 from .comm import CommManager
 from .kernelbase import Kernel as KernelBase
 from .zmqshell import ZMQInteractiveShell
 
 
+try:
+    from IPython.core.completer import rectify_completions as _rectify_completions, provisionalcompleter as _provisionalcompleter
+    _use_experimental_60_completion = True
+except ImportError:
+    _use_experimental_60_completion = False
+
+_EXPERIMENTAL_KEY_NAME = '_jupyter_types_experimental'
+
+
 class IPythonKernel(KernelBase):
     shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
                      allow_none=True)
     shell_class = Type(ZMQInteractiveShell)
 
+    use_experimental_completions = Bool(True,
+        help="Set this flag to False to deactivate the use of experimental IPython completion APIs.",
+    ).tag(config=True)
+
     user_module = Any()
     def _user_module_changed(self, name, old, new):
         if self.shell is not None:
@@ -60,32 +72,32 @@ class IPythonKernel(KernelBase):
 
     help_links = List([
         {
-            'text': "Python",
-            'url': "http://docs.python.org/%i.%i" % sys.version_info[:2],
+            'text': "Python Reference",
+            'url': "https://docs.python.org/%i.%i" % sys.version_info[:2],
         },
         {
-            'text': "IPython",
-            'url': "http://ipython.org/documentation.html",
+            'text': "IPython Reference",
+            'url': "https://ipython.org/documentation.html",
         },
         {
-            'text': "NumPy",
-            'url': "http://docs.scipy.org/doc/numpy/reference/",
+            'text': "NumPy Reference",
+            'url': "https://docs.scipy.org/doc/numpy/reference/",
         },
         {
-            'text': "SciPy",
-            'url': "http://docs.scipy.org/doc/scipy/reference/",
+            'text': "SciPy Reference",
+            'url': "https://docs.scipy.org/doc/scipy/reference/",
         },
         {
-            'text': "Matplotlib",
-            'url': "http://matplotlib.org/contents.html",
+            'text': "Matplotlib Reference",
+            'url': "https://matplotlib.org/contents.html",
         },
         {
-            'text': "SymPy",
+            'text': "SymPy Reference",
             'url': "http://docs.sympy.org/latest/index.html",
         },
         {
-            'text': "pandas",
-            'url': "http://pandas.pydata.org/pandas-docs/stable/",
+            'text': "pandas Reference",
+            'url': "https://pandas.pydata.org/pandas-docs/stable/",
         },
     ]).tag(config=True)
 
@@ -246,6 +258,9 @@ class IPythonKernel(KernelBase):
         return reply_content
 
     def do_complete(self, code, cursor_pos):
+        if _use_experimental_60_completion and self.use_experimental_completions:
+            return self._experimental_do_complete(code, cursor_pos)
+
         # FIXME: IPython completers currently assume single line,
         # but completion messages give multi-line context
         # For now, extract line from cell, based on cursor_pos:
@@ -261,6 +276,42 @@ class IPythonKernel(KernelBase):
                 'metadata' : {},
                 'status' : 'ok'}
 
+    def _experimental_do_complete(self, code, cursor_pos):
+        """
+        Experimental completions from IPython, using Jedi. 
+        """
+        if cursor_pos is None:
+            cursor_pos = len(code)
+        with _provisionalcompleter():
+            raw_completions = self.shell.Completer.completions(code, cursor_pos)
+            completions = list(_rectify_completions(code, raw_completions))
+            
+            comps = []
+            for comp in completions:
+                comps.append(dict(
+                            start=comp.start,
+                            end=comp.end,
+                            text=comp.text,
+                            type=comp.type,
+                ))
+
+        if completions:
+            s = completions[0].start
+            e = completions[0].end
+            matches = [c.text for c in completions]
+        else:
+            s = cursor_pos
+            e = cursor_pos
+            matches = []
+
+        return {'matches': matches,
+                'cursor_end': e,
+                'cursor_start': s,
+                'metadata': {_EXPERIMENTAL_KEY_NAME: comps},
+                'status': 'ok'}
+
+
+
     def do_inspect(self, code, cursor_pos, detail_level=0):
         name = token_at_cursor(code, cursor_pos)
         info = self.shell.object_inspect(name)
@@ -304,7 +355,7 @@ class IPythonKernel(KernelBase):
         return dict(status='ok', restart=restart)
 
     def do_is_complete(self, code):
-        status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
+        status, indent_spaces = self.shell.input_splitter.check_complete(code)
         r = {'status': status}
         if status == 'incomplete':
             r['indent'] = ' ' * indent_spaces
diff --git a/ipykernel/jsonutil.py b/ipykernel/jsonutil.py
index 3121e53..df669f7 100644
--- a/ipykernel/jsonutil.py
+++ b/ipykernel/jsonutil.py
@@ -3,18 +3,13 @@
 # Copyright (c) IPython Development Team.
 # Distributed under the terms of the Modified BSD License.
 
+from binascii import b2a_base64
 import math
 import re
 import types
 from datetime import datetime
 import numbers
 
-try:
-    # base64.encodestring is deprecated in Python 3.x
-    from base64 import encodebytes
-except ImportError:
-    # Python 2.x
-    from base64 import encodestring as encodebytes
 
 from ipython_genutils import py3compat
 from ipython_genutils.py3compat import unicode_type, iteritems
@@ -45,6 +40,9 @@ PNG64 = b'iVBORw0KG'
 JPEG = b'\xff\xd8'
 # front of JPEG base64-encoded
 JPEG64 = b'/9'
+# constants for identifying gif data
+GIF_64 = b'R0lGODdh'
+GIF89_64 = b'R0lGODlh'
 # front of PDF base64-encoded
 PDF64 = b'JVBER'
 
@@ -68,27 +66,41 @@ def encode_images(format_dict):
         is base64-encoded.
 
     """
+
+    # no need for handling of ambiguous bytestrings on Python 3,
+    # where bytes objects always represent binary data and thus
+    # base64-encoded.
+    if py3compat.PY3:
+        return format_dict
+
     encoded = format_dict.copy()
 
     pngdata = format_dict.get('image/png')
     if isinstance(pngdata, bytes):
         # make sure we don't double-encode
         if not pngdata.startswith(PNG64):
-            pngdata = encodebytes(pngdata)
+            pngdata = b2a_base64(pngdata)
         encoded['image/png'] = pngdata.decode('ascii')
 
     jpegdata = format_dict.get('image/jpeg')
     if isinstance(jpegdata, bytes):
         # make sure we don't double-encode
         if not jpegdata.startswith(JPEG64):
-            jpegdata = encodebytes(jpegdata)
+            jpegdata = b2a_base64(jpegdata)
         encoded['image/jpeg'] = jpegdata.decode('ascii')
+        
+    gifdata = format_dict.get('image/gif')
+    if isinstance(gifdata, bytes):
+        # make sure we don't double-encode
+        if not gifdata.startswith((GIF_64, GIF89_64)):
+            gifdata = b2a_base64(gifdata)
+        encoded['image/gif'] = gifdata.decode('ascii')
 
     pdfdata = format_dict.get('application/pdf')
     if isinstance(pdfdata, bytes):
         # make sure we don't double-encode
         if not pdfdata.startswith(PDF64):
-            pdfdata = encodebytes(pdfdata)
+            pdfdata = b2a_base64(pdfdata)
         encoded['application/pdf'] = pdfdata.decode('ascii')
 
     return encoded
@@ -141,9 +153,21 @@ def json_clean(obj):
     
     if isinstance(obj, atomic_ok):
         return obj
-
+    
     if isinstance(obj, bytes):
-        return obj.decode(DEFAULT_ENCODING, 'replace')
+        if py3compat.PY3:
+            # unanmbiguous binary data is base64-encoded
+            # (this probably should have happened upstream)
+            return b2a_base64(obj).decode('ascii')
+        else:
+            # Python 2 bytestr is ambiguous,
+            # needs special handling for possible binary bytestrings.
+            # imperfect workaround: if ascii, assume text.
+            # otherwise assume binary, base64-encode (py3 behavior).
+            try:
+                return obj.decode('ascii')
+            except UnicodeDecodeError:
+                return b2a_base64(obj).decode('ascii')
 
     if isinstance(obj, container_to_list) or (
         hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
diff --git a/ipykernel/kernelapp.py b/ipykernel/kernelapp.py
index 59190c0..b67d150 100644
--- a/ipykernel/kernelapp.py
+++ b/ipykernel/kernelapp.py
@@ -473,8 +473,9 @@ class IPKernelApp(BaseIPythonApplication, InteractiveShellApp,
         if self.poller is not None:
             self.poller.start()
         self.kernel.start()
+        self.io_loop = ioloop.IOLoop.current()
         try:
-            ioloop.IOLoop.instance().start()
+            self.io_loop.start()
         except KeyboardInterrupt:
             pass
 
diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py
index 90f33cf..199af5c 100644
--- a/ipykernel/kernelbase.py
+++ b/ipykernel/kernelbase.py
@@ -49,7 +49,7 @@ class Kernel(SingletonConfigurable):
     @observe('eventloop')
     def _update_eventloop(self, change):
         """schedule call to eventloop from IOLoop"""
-        loop = ioloop.IOLoop.instance()
+        loop = ioloop.IOLoop.current()
         loop.add_callback(self.enter_eventloop)
 
     session = Instance(Session, allow_none=True)
@@ -135,7 +135,6 @@ class Kernel(SingletonConfigurable):
 
     def __init__(self, **kwargs):
         super(Kernel, self).__init__(**kwargs)
-
         # Build dict of handlers for message types
         self.shell_handlers = {}
         for msg_type in self.msg_types:
@@ -212,8 +211,6 @@ class Kernel(SingletonConfigurable):
         self.set_parent(idents, msg)
         self._publish_status(u'busy')
 
-        header = msg['header']
-        msg_id = header['msg_id']
         msg_type = msg['header']['msg_type']
 
         # Print some info about this message and leave a '--->' marker, so it's
@@ -275,6 +272,7 @@ class Kernel(SingletonConfigurable):
 
     def start(self):
         """register dispatchers for streams"""
+        self.io_loop = ioloop.IOLoop.current()
         if self.control_stream:
             self.control_stream.on_recv(self.dispatch_control, copy=False)
 
@@ -430,12 +428,11 @@ class Kernel(SingletonConfigurable):
         content = parent['content']
         code = content['code']
         cursor_pos = content['cursor_pos']
-
+        
         matches = self.do_complete(code, cursor_pos)
         matches = json_clean(matches)
         completion_msg = self.session.send(stream, 'complete_reply',
                                            matches, parent, ident)
-        self.log.debug("%s", completion_msg)
 
     def do_complete(self, code, cursor_pos):
         """Override in subclasses to find completions.
@@ -534,7 +531,7 @@ class Kernel(SingletonConfigurable):
 
         self._at_shutdown()
         # call sys.exit after a short delay
-        loop = ioloop.IOLoop.instance()
+        loop = ioloop.IOLoop.current()
         loop.add_timeout(time.time()+0.1, loop.stop)
 
     def do_shutdown(self, restart):
diff --git a/ipykernel/pylab/backend_inline.py b/ipykernel/pylab/backend_inline.py
index 63b9693..739165e 100644
--- a/ipykernel/pylab/backend_inline.py
+++ b/ipykernel/pylab/backend_inline.py
@@ -150,12 +150,14 @@ def _enable_matplotlib_integration():
     ip = get_ipython()
     backend = get_backend()
     if ip and backend == 'module://%s' % __name__:
-        from IPython.core.pylabtools import configure_inline_support
+        from IPython.core.pylabtools import configure_inline_support, activate_matplotlib
         try:
+            activate_matplotlib(backend)
             configure_inline_support(ip, backend)
-        except ImportError:
+        except (ImportError, AttributeError):
             # bugs may cause a circular import on Python 2
             def configure_once(*args):
+                activate_matplotlib(backend)
                 configure_inline_support(ip, backend)
                 ip.events.unregister('post_run_cell', configure_once)
             ip.events.register('post_run_cell', configure_once)
diff --git a/ipykernel/tests/_asyncio.py b/ipykernel/tests/_asyncio.py
new file mode 100644
index 0000000..43e3229
--- /dev/null
+++ b/ipykernel/tests/_asyncio.py
@@ -0,0 +1,17 @@
+"""test utilities that use async/await syntax
+
+a separate file to avoid syntax errors on Python 2
+"""
+
+import asyncio
+
+
+def async_func():
+    """Simple async function to schedule a task on the current eventloop"""
+    loop = asyncio.get_event_loop()
+    assert loop.is_running()
+
+    async def task():
+        await asyncio.sleep(1)
+
+    loop.create_task(task())
diff --git a/ipykernel/tests/test_connect.py b/ipykernel/tests/test_connect.py
index e0b812c..739eb12 100644
--- a/ipykernel/tests/test_connect.py
+++ b/ipykernel/tests/test_connect.py
@@ -6,8 +6,6 @@
 import json
 import os
 
-import nose.tools as nt
-
 from traitlets.config import Config
 from ipython_genutils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
 from ipython_genutils.py3compat import str_to_bytes
@@ -36,14 +34,14 @@ def test_get_connection_file():
         app.initialize()
 
         profile_cf = os.path.join(app.connection_dir, cf)
-        nt.assert_equal(profile_cf, app.abs_connection_file)
+        assert profile_cf == app.abs_connection_file
         with open(profile_cf, 'w') as f:
             f.write("{}")
-        nt.assert_true(os.path.exists(profile_cf))
-        nt.assert_equal(connect.get_connection_file(app), profile_cf)
+        assert os.path.exists(profile_cf)
+        assert connect.get_connection_file(app) == profile_cf
 
         app.connection_file = cf
-        nt.assert_equal(connect.get_connection_file(app), profile_cf)
+        assert connect.get_connection_file(app) == profile_cf
 
 
 def test_get_connection_info():
@@ -52,12 +50,12 @@ def test_get_connection_info():
         connect.write_connection_file(cf, **sample_info)
         json_info = connect.get_connection_info(cf)
         info = connect.get_connection_info(cf, unpack=True)
-    
-    nt.assert_equal(type(json_info), type(""))
+    assert isinstance(json_info, str)
+
     sub_info = {k:v for k,v in info.items() if k in sample_info}
-    nt.assert_equal(sub_info, sample_info)
+    assert sub_info == sample_info
 
     info2 = json.loads(json_info)
     info2['key'] = str_to_bytes(info2['key'])
     sub_info2 = {k:v for k,v in info.items() if k in sample_info}
-    nt.assert_equal(sub_info2, sample_info)
+    assert sub_info2 == sample_info
diff --git a/ipykernel/tests/test_embed_kernel.py b/ipykernel/tests/test_embed_kernel.py
index 03de53d..5e60245 100644
--- a/ipykernel/tests/test_embed_kernel.py
+++ b/ipykernel/tests/test_embed_kernel.py
@@ -4,19 +4,14 @@
 # Distributed under the terms of the Modified BSD License.
 
 import os
-import shutil
 import sys
-import tempfile
 import time
 
 from contextlib import contextmanager
 from subprocess import Popen, PIPE
 
-import nose.tools as nt
-
 from jupyter_client import BlockingKernelClient
 from jupyter_core import paths
-from IPython.paths import get_ipython_dir
 from ipython_genutils import py3compat
 from ipython_genutils.py3compat import unicode_type
 
@@ -83,20 +78,20 @@ def test_embed_kernel_basic():
         msg_id = client.inspect('a')
         msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
         content = msg['content']
-        nt.assert_true(content['found'])
+        assert content['found']
 
         msg_id = client.execute("c=a*2")
         msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
         content = msg['content']
-        nt.assert_equal(content['status'], u'ok')
+        assert content['status'] == u'ok'
 
         # oinfo c (should be 10)
         msg_id = client.inspect('c')
         msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
         content = msg['content']
-        nt.assert_true(content['found'])
+        assert content['found']
         text = content['data']['text/plain']
-        nt.assert_in('10', text)
+        assert '10' in text
 
 def test_embed_kernel_namespace():
     """IPython.embed_kernel() inherits calling namespace"""
@@ -115,23 +110,23 @@ def test_embed_kernel_namespace():
         msg_id = client.inspect('a')
         msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
         content = msg['content']
-        nt.assert_true(content['found'])
+        assert content['found']
         text = content['data']['text/plain']
-        nt.assert_in(u'5', text)
+        assert u'5' in text
 
         # oinfo b (str)
         msg_id = client.inspect('b')
         msg = client.get_shell_msg(block=True, timeout=TIMEOUT)
         content = msg['content']
-        nt.assert_true(content['found'])
+        assert content['found']
         text = content['data']['text/plain']
-        nt.assert_in(u'hi there', text)
+        assert u'hi there' in text
 
... 1156 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/ipykernel.git



More information about the Python-modules-commits mailing list