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

Gordon Ball chronitis-guest at moszumanska.debian.org
Tue Feb 6 15:11:09 UTC 2018


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

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

commit 669a5bd37c9d020646d02a2d3eb0a88c74758f2d
Author: Gordon Ball <gordon at chronitis.net>
Date:   Tue Feb 6 11:02:13 2018 +0000

    Import ipykernel_4.8.0.orig.tar.gz
---
 MANIFEST.in                   |   2 +-
 docs/changelog.rst            |  17 +++++
 docs/conf.py                  |   3 +
 docs/requirements.txt         |   1 +
 ipykernel/_eventloop_macos.py | 153 ++++++++++++++++++++++++++++++++++++++++++
 ipykernel/_version.py         |   2 +-
 ipykernel/eventloops.py       | 117 +++++++++++++++++++-------------
 ipykernel/kernelbase.py       |   6 +-
 ipykernel/zmqshell.py         |   4 ++
 readthedocs.yml               |   1 +
 10 files changed, 256 insertions(+), 50 deletions(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index ce535d5..90bc61e 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -10,7 +10,7 @@ exclude docs/\#*
 graft examples
 
 # docs subdirs we want to skip
-prune docs/build
+prune docs/_build
 prune docs/gh-pages
 prune docs/dist
 
diff --git a/docs/changelog.rst b/docs/changelog.rst
index a5d950d..40f675c 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,21 @@
 Changes in IPython kernel
 =========================
 
+4.8
+---
+
+4.8.0
+*****
+
+`4.8.0 on GitHub <https://github.com/ipython/ipykernel/milestones/4.8>`__
+
+- Cleanly shutdown integrated event loops when shutting down the kernel.
+  (:ghpull:`290`)
+- ``%gui qt`` now uses Qt 5 by default rather than Qt 4, following a similar
+  change in terminal IPython. (:ghpull:`293`)
+- Fix event loop integration for :mod:`asyncio` when run with Tornado 5,
+  which uses asyncio where available. (:ghpull:`296`)
+
 4.7
 ---
 
@@ -38,8 +53,10 @@ Changes in IPython kernel
 
 - Add to API `DisplayPublisher.publish` two new fully backward-compatible
   keyword-args:
+
     - `update: bool`
     - `transient: dict`
+
 - Support new `transient` key in `display_data` messages spec for `publish`.
   For a display data message, `transient` contains data that shouldn't be
   persisted to files or documents. Add a `display_id` to this `transient`
diff --git a/docs/conf.py b/docs/conf.py
index 4c03611..d7a215b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -33,8 +33,11 @@ import shlex
 extensions = [
     'sphinx.ext.autodoc',
     'sphinx.ext.intersphinx',
+    'sphinxcontrib_github_alt',
 ]
 
+github_project_url = "https://github.com/ipython/ipykernel"
+
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
 
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..623e487
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1 @@
+sphinxcontrib_github_alt
diff --git a/ipykernel/_eventloop_macos.py b/ipykernel/_eventloop_macos.py
new file mode 100644
index 0000000..fbae6ac
--- /dev/null
+++ b/ipykernel/_eventloop_macos.py
@@ -0,0 +1,153 @@
+"""Eventloop hook for OS X
+
+Calls NSApp / CoreFoundation APIs via ctypes.
+"""
+
+# cribbed heavily from IPython.terminal.pt_inputhooks.osx
+# obj-c boilerplate from appnope, used under BSD 2-clause
+
+import ctypes
+import ctypes.util
+from threading import Event
+
+objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
+
+void_p = ctypes.c_void_p
+
+objc.objc_getClass.restype = void_p
+objc.sel_registerName.restype = void_p
+objc.objc_msgSend.restype = void_p
+objc.objc_msgSend.argtypes = [void_p, void_p]
+
+msg = objc.objc_msgSend
+
+
+def _utf8(s):
+    """ensure utf8 bytes"""
+    if not isinstance(s, bytes):
+        s = s.encode('utf8')
+    return s
+
+
+def n(name):
+    """create a selector name (for ObjC methods)"""
+    return objc.sel_registerName(_utf8(name))
+
+
+def C(classname):
+    """get an ObjC Class by name"""
+    return objc.objc_getClass(_utf8(classname))
+
+
+# end obj-c boilerplate from appnope
+
+# CoreFoundation C-API calls we will use:
+CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
+
+CFAbsoluteTimeGetCurrent = CoreFoundation.CFAbsoluteTimeGetCurrent
+CFAbsoluteTimeGetCurrent.restype = ctypes.c_double
+
+CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
+CFRunLoopGetCurrent.restype = void_p
+
+CFRunLoopGetMain = CoreFoundation.CFRunLoopGetMain
+CFRunLoopGetMain.restype = void_p
+
+CFRunLoopStop = CoreFoundation.CFRunLoopStop
+CFRunLoopStop.restype = None
+CFRunLoopStop.argtypes = [void_p]
+
+CFRunLoopTimerCreate = CoreFoundation.CFRunLoopTimerCreate
+CFRunLoopTimerCreate.restype = void_p
+CFRunLoopTimerCreate.argtypes = [
+    void_p,             # allocator (NULL)
+    ctypes.c_double,    # fireDate
+    ctypes.c_double,    # interval
+    ctypes.c_int,       # flags (0)
+    ctypes.c_int,       # order (0)
+    void_p,             # callout
+    void_p,             # context
+]
+
+CFRunLoopAddTimer = CoreFoundation.CFRunLoopAddTimer
+CFRunLoopAddTimer.restype = None
+CFRunLoopAddTimer.argtypes = [ void_p, void_p, void_p ]
+
+kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, 'kCFRunLoopCommonModes')
+
+
+def _NSApp():
+    """Return the global NSApplication instance (NSApp)"""
+    return msg(C('NSApplication'), n('sharedApplication'))
+
+
+def _wake(NSApp):
+    """Wake the Application"""
+    event = msg(C('NSEvent'),
+        n('otherEventWithType:location:modifierFlags:'
+          'timestamp:windowNumber:context:subtype:data1:data2:'),
+        15, # Type
+        0, # location
+        0, # flags
+        0, # timestamp
+        0, # window
+        None, # context
+        0, # subtype
+        0, # data1
+        0, # data2
+    )
+    msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
+
+
+_triggered = Event()
+
+
+def stop(timer=None, loop=None):
+    """Callback to fire when there's input to be read"""
+    _triggered.set()
+    NSApp = _NSApp()
+    # if NSApp is not running, stop CFRunLoop directly,
+    # otherwise stop and wake NSApp
+    if msg(NSApp, n('isRunning')):
+        msg(NSApp, n('stop:'), NSApp)
+        _wake(NSApp)
+    else:
+        CFRunLoopStop(CFRunLoopGetCurrent())
+
+
+_c_callback_func_type = ctypes.CFUNCTYPE(None, void_p, void_p)
+_c_stop_callback = _c_callback_func_type(stop)
+
+
+def _stop_after(delay):
+    """Register callback to stop eventloop after a delay"""
+    timer = CFRunLoopTimerCreate(
+        None, # allocator
+        CFAbsoluteTimeGetCurrent() + delay, # fireDate
+        0, # interval
+        0, # flags
+        0, # order
+        _c_stop_callback,
+        None,
+    )
+    CFRunLoopAddTimer(
+        CFRunLoopGetMain(),
+        timer,
+        kCFRunLoopCommonModes,
+    )
+
+
+def mainloop(duration=1):
+    """run the Cocoa eventloop for the specified duration (seconds)"""
+
+    _triggered.clear()
+    NSApp = _NSApp()
+    _stop_after(duration)
+    msg(NSApp, n('run'))
+    if not _triggered.is_set():
+        # app closed without firing callback,
+        # probably due to last window being closed.
+        # Run the loop manually in this case,
+        # since there may be events still to process (ipython/ipython#9734)
+        CoreFoundation.CFRunLoopRun()
+
diff --git a/ipykernel/_version.py b/ipykernel/_version.py
index 645a51e..df91ce1 100644
--- a/ipykernel/_version.py
+++ b/ipykernel/_version.py
@@ -1,4 +1,4 @@
-version_info = (4, 7, 0)
+version_info = (4, 8, 0)
 __version__ = '.'.join(map(str, version_info))
 
 kernel_protocol_version_info = (5, 1)
diff --git a/ipykernel/eventloops.py b/ipykernel/eventloops.py
index 86f21cd..47b6d72 100644
--- a/ipykernel/eventloops.py
+++ b/ipykernel/eventloops.py
@@ -49,7 +49,7 @@ loop_map = {
     'notebook': None,
     'ipympl': None,
     'widget': None,
-    None : None,
+    None: None,
 }
 
 def register_integration(*toolkitnames):
@@ -68,6 +68,17 @@ def register_integration(*toolkitnames):
     def decorator(func):
         for name in toolkitnames:
             loop_map[name] = func
+
+        func.exit_hook = lambda kernel: None
+
+        def exit_decorator(exit_func):
+            """@func.exit is now a decorator
+
+            to register a function to be called on exit
+            """
+            func.exit_hook = exit_func
+
+        func.exit = exit_decorator
         return func
 
     return decorator
@@ -85,7 +96,7 @@ def _loop_qt(app):
     app._in_event_loop = False
 
 
- at register_integration('qt', 'qt4')
+ at register_integration('qt4')
 def loop_qt4(kernel):
     """Start a kernel with PyQt4 event loop integration."""
 
@@ -100,13 +111,23 @@ def loop_qt4(kernel):
     _loop_qt(kernel.app)
 
 
- at register_integration('qt5')
+ at loop_qt4.exit
+def loop_qt4_exit(kernel):
+    kernel.app.exit()
+
+
+ at register_integration('qt', 'qt5')
 def loop_qt5(kernel):
     """Start a kernel with PyQt5 event loop integration."""
     os.environ['QT_API'] = 'pyqt5'
     return loop_qt4(kernel)
 
 
+ at loop_qt5.exit
+def loop_qt5_exit(kernel):
+    kernel.app.exit()
+
+
 def _loop_wx(app):
     """Inner-loop for running the Wx eventloop
 
@@ -171,6 +192,12 @@ def loop_wx(kernel):
     _loop_wx(kernel.app)
 
 
+ at loop_wx.exit
+def loop_wx_exit(kernel):
+    import wx
+    wx.Exit()
+
+
 @register_integration('tk')
 def loop_tk(kernel):
     """Start a kernel with the Tk event loop."""
@@ -201,6 +228,11 @@ def loop_tk(kernel):
     kernel.timer.start()
 
 
+ at loop_tk.exit
+def loop_tk_exit(kernel):
+    kernel.timer.app.destroy()
+
+
 @register_integration('gtk')
 def loop_gtk(kernel):
     """Start the kernel, coordinating with the GTK event loop"""
@@ -208,6 +240,12 @@ def loop_gtk(kernel):
 
     gtk_kernel = GTKEmbed(kernel)
     gtk_kernel.start()
+    kernel._gtk = gtk_kernel
+
+
+ at loop_gtk.exit
+def loop_gtk_exit(kernel):
+    kernel._gtk.stop()
 
 
 @register_integration('gtk3')
@@ -217,6 +255,12 @@ def loop_gtk3(kernel):
 
     gtk_kernel = GTKEmbed(kernel)
     gtk_kernel.start()
+    kernel._gtk = gtk_kernel
+
+
+ at loop_gtk3.exit
+def loop_gtk3_exit(kernel):
+    kernel._gtk.stop()
 
 
 @register_integration('osx')
@@ -224,62 +268,27 @@ def loop_cocoa(kernel):
     """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
     via the matplotlib MacOSX backend.
     """
-    import matplotlib
-    if matplotlib.__version__ < '1.1.0':
-        kernel.log.warn(
-        "MacOSX backend in matplotlib %s doesn't have a Timer, "
-        "falling back on Tk for CFRunLoop integration.  Note that "
-        "even this won't work if Tk is linked against X11 instead of "
-        "Cocoa (e.g. EPD).  To use the MacOSX backend in the kernel, "
-        "you must use matplotlib >= 1.1.0, or a native libtk."
-        )
-        return loop_tk(kernel)
-
-    from matplotlib.backends.backend_macosx import TimerMac, show
-
-    # scale interval for sec->ms
-    poll_interval = int(1000*kernel._poll_interval)
+    from ._eventloop_macos import mainloop, stop
 
     real_excepthook = sys.excepthook
     def handle_int(etype, value, tb):
         """don't let KeyboardInterrupts look like crashes"""
+        # wake the eventloop when we get a signal
+        stop()
         if etype is KeyboardInterrupt:
             io.raw_print("KeyboardInterrupt caught in CFRunLoop")
         else:
             real_excepthook(etype, value, tb)
 
-    # add doi() as a Timer to the CFRunLoop
-    def doi():
-        # restore excepthook during IPython code
-        sys.excepthook = real_excepthook
-        kernel.do_one_iteration()
-        # and back:
-        sys.excepthook = handle_int
-
-    t = TimerMac(poll_interval)
-    t.add_callback(doi)
-    t.start()
-
-    # but still need a Poller for when there are no active windows,
-    # during which time mainloop() returns immediately
-    poller = zmq.Poller()
-    if kernel.control_stream:
-        poller.register(kernel.control_stream.socket, zmq.POLLIN)
-    for stream in kernel.shell_streams:
-        poller.register(stream.socket, zmq.POLLIN)
-
-    while True:
+    while not kernel.shell.exit_now:
         try:
             # double nested try/except, to properly catch KeyboardInterrupt
             # due to pyzmq Issue #130
             try:
                 # don't let interrupts during mainloop invoke crash_handler:
                 sys.excepthook = handle_int
-                show.mainloop()
+                mainloop(kernel._poll_interval)
                 sys.excepthook = real_excepthook
-                # use poller if mainloop returned (no windows)
-                # scale by extra factor of 10, since it's a real poll
-                poller.poll(10*poll_interval)
                 kernel.do_one_iteration()
             except:
                 raise
@@ -291,20 +300,26 @@ def loop_cocoa(kernel):
             sys.excepthook = real_excepthook
 
 
+ at loop_cocoa.exit
+def loop_cocoa_exit(kernel):
+    from ._eventloop_macos import stop
+    stop()
+
+
 @register_integration('asyncio')
 def loop_asyncio(kernel):
     '''Start a kernel with asyncio event loop support.'''
     import asyncio
     loop = asyncio.get_event_loop()
+    # loop is already running (e.g. tornado 5), nothing left to do
+    if loop.is_running():
+        return
 
     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:
@@ -320,6 +335,16 @@ def loop_asyncio(kernel):
             raise error
         break
 
+
+ at loop_asyncio.exit
+def loop_asyncio_exit(kernel):
+    """Exit hook for asyncio"""
+    import asyncio
+    loop = asyncio.get_event_loop()
+    if loop.is_running():
+        loop.call_soon(loop.stop)
+
+
 def enable_gui(gui, kernel=None):
     """Enable integration with a given GUI"""
     if gui not in loop_map:
diff --git a/ipykernel/kernelbase.py b/ipykernel/kernelbase.py
index 199af5c..cf75e10 100644
--- a/ipykernel/kernelbase.py
+++ b/ipykernel/kernelbase.py
@@ -50,7 +50,8 @@ class Kernel(SingletonConfigurable):
     def _update_eventloop(self, change):
         """schedule call to eventloop from IOLoop"""
         loop = ioloop.IOLoop.current()
-        loop.add_callback(self.enter_eventloop)
+        if change.new is not None:
+            loop.add_callback(self.enter_eventloop)
 
     session = Instance(Session, allow_none=True)
     profile_dir = Instance('IPython.core.profiledir.ProfileDir', allow_none=True)
@@ -256,7 +257,7 @@ class Kernel(SingletonConfigurable):
             # which may be skipped by entering the eventloop
             stream.flush(zmq.POLLOUT)
         # restore default_int_handler
-        signal(SIGINT, default_int_handler)
+        self.pre_handler_hook()
         while self.eventloop is not None:
             try:
                 self.eventloop(self)
@@ -268,6 +269,7 @@ class Kernel(SingletonConfigurable):
                 # eventloop exited cleanly, this means we should stop (right?)
                 self.eventloop = None
                 break
+        self.post_handler_hook()
         self.log.info("exiting eventloop")
 
     def start(self):
diff --git a/ipykernel/zmqshell.py b/ipykernel/zmqshell.py
index 7d25d4d..3cdf6e6 100644
--- a/ipykernel/zmqshell.py
+++ b/ipykernel/zmqshell.py
@@ -471,6 +471,10 @@ class ZMQInteractiveShell(InteractiveShell):
         if change['new']:
             loop = self.kernel.io_loop
             loop.call_later(0.1, loop.stop)
+            if self.kernel.eventloop:
+                exit_hook = getattr(self.kernel.eventloop, 'exit_hook', None)
+                if exit_hook:
+                    exit_hook(self.kernel)
 
     keepkernel_on_exit = None
 
diff --git a/readthedocs.yml b/readthedocs.yml
index f8b3b41..a2bc264 100644
--- a/readthedocs.yml
+++ b/readthedocs.yml
@@ -1,3 +1,4 @@
 python:
   version: 3.5
   pip_install: true
+requirements_file: docs/requirements.txt

-- 
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