From 72366b31b42b9521a58c1e82fc8b420ad1843c37 2013-11-13 19:48:19 From: MinRK Date: 2013-11-13 19:48:19 Subject: [PATCH] use QSocketNotifier, not poll avoids having to disable App Nap outright. Also use appnope context manager to disable App Nap on OS X 10.9 while processing events. Adds IPKernel._darwin_app_nap flag, so that working with App Nap can be disabled, in case appnope causes problems for some. App Nap is just disabled outright on wx. --- diff --git a/IPython/kernel/zmq/eventloops.py b/IPython/kernel/zmq/eventloops.py index 0335075..f3a437c 100644 --- a/IPython/kernel/zmq/eventloops.py +++ b/IPython/kernel/zmq/eventloops.py @@ -16,10 +16,10 @@ import sys -# System library imports. +# System library imports import zmq -# Local imports. +# Local imports from IPython.config.application import Application from IPython.utils import io @@ -28,18 +28,40 @@ from IPython.utils import io # Eventloops for integrating the Kernel into different GUIs #------------------------------------------------------------------------------ +def _on_os_x_10_9(): + import platform + from distutils.version import LooseVersion as V + return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9') + +def _notify_stream_qt(kernel, stream): + + from IPython.external.qt_for_kernel import QtCore + + if _on_os_x_10_9() and kernel._darwin_app_nap: + from IPython.external.appnope import nope_scope as context + else: + from IPython.core.interactiveshell import no_op_context as context + + def process_stream_events(): + while stream.getsockopt(zmq.EVENTS) & zmq.POLLIN: + with context(): + kernel.do_one_iteration() + + fd = stream.getsockopt(zmq.FD) + notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read, kernel.app) + notifier.activated.connect(process_stream_events) + def loop_qt4(kernel): """Start a kernel with PyQt4 event loop integration.""" - from IPython.external.qt_for_kernel import QtCore from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4 kernel.app = get_app_qt4([" "]) kernel.app.setQuitOnLastWindowClosed(False) - kernel.timer = QtCore.QTimer() - kernel.timer.timeout.connect(kernel.do_one_iteration) - # Units for the timer are in milliseconds - kernel.timer.start(1000*kernel._poll_interval) + + for s in kernel.shell_streams: + _notify_stream_qt(kernel, s) + start_event_loop_qt4(kernel.app) @@ -48,6 +70,12 @@ def loop_wx(kernel): import wx from IPython.lib.guisupport import start_event_loop_wx + + if _on_os_x_10_9() and kernel._darwin_app_nap: + # we don't hook up App Nap contexts for Wx, + # just disable it outright. + from IPython.external.appnope import nope + nope() doi = kernel.do_one_iteration # Wx uses milliseconds diff --git a/IPython/kernel/zmq/ipkernel.py b/IPython/kernel/zmq/ipkernel.py index 2a71dcf..15840bb 100755 --- a/IPython/kernel/zmq/ipkernel.py +++ b/IPython/kernel/zmq/ipkernel.py @@ -32,7 +32,7 @@ from IPython.utils.py3compat import builtin_mod, unicode_type, string_types from IPython.utils.jsonutil import json_clean from IPython.utils.traitlets import ( Any, Instance, Float, Dict, List, Set, Integer, Unicode, - Type + Type, Bool, ) from .serialize import serialize_object, unpack_apply_message @@ -91,9 +91,15 @@ class Kernel(Configurable): def _ident_default(self): return unicode_type(uuid.uuid4()) - # Private interface + _darwin_app_nap = Bool(True, config=True, + help="""Whether to use appnope for compatiblity with OS X App Nap. + + Only affects OS X >= 10.9. + """ + ) + # Time to sleep after flushing the stdout/err buffers in each execute # cycle. While this introduces a hard limit on the minimal latency of the # execute cycle, it helps prevent output synchronization problems for diff --git a/IPython/kernel/zmq/kernelapp.py b/IPython/kernel/zmq/kernelapp.py index 44ae2d2..bb707ce 100644 --- a/IPython/kernel/zmq/kernelapp.py +++ b/IPython/kernel/zmq/kernelapp.py @@ -424,13 +424,7 @@ class IPKernelApp(BaseIPythonApplication, InteractiveShellApp): def init_shell(self): self.shell = self.kernel.shell self.shell.configurables.append(self) - - def init_osx(self): - if sys.platform != 'darwin': - return - from IPython.utils.darwin import disable_app_nap - self._activity = disable_app_nap(self.log.warn) - + @catch_config_error def initialize(self, argv=None): super(IPKernelApp, self).initialize(argv) @@ -445,7 +439,6 @@ class IPKernelApp(BaseIPythonApplication, InteractiveShellApp): self.write_connection_file() self.init_io() self.init_signal() - self.init_osx() self.init_kernel() # shell init steps self.init_path() diff --git a/IPython/utils/darwin.py b/IPython/utils/darwin.py deleted file mode 100644 index bac65a6..0000000 --- a/IPython/utils/darwin.py +++ /dev/null @@ -1,64 +0,0 @@ -# encoding: utf-8 -"""Utilities for working on darwin platforms (OS X)""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2013 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -import platform -import sys -from distutils.version import LooseVersion as V - -#----------------------------------------------------------------------------- -# Classes and functions -#----------------------------------------------------------------------------- - -def on_10_9(): - """Are we on OS X 10.9 or greater?""" - vs = platform.mac_ver()[0] - if not vs: - return False - if V(vs) >= V('10.9'): - return True - return False - -def disable_app_nap(log=None): - """Disable OS X 10.9 App Nap energy saving feature - - App Nap can cause problems with interactivity when using GUI eventloops - in the Kernel, and possibly in other scenarios as well. - - Returns the NSActivity object, which can be used to end the condition - via ``NSProcessInfo.endActivity_(activity)``. - """ - - if sys.platform != 'darwin' or not on_10_9(): - return - - try: - from Foundation import NSProcessInfo - except ImportError: - if log is not None: - log("Could not import NSProcessInfo." - " PyObjC is needed to disable App Nap on OS X 10.9") - return - - # copy constants from CoreFoundation docs - # these should be imported, but they don't seem to be exposed by PyObjC - NSActivityIdleSystemSleepDisabled = 1 << 20 - NSActivityUserInitiated = 0x00FFFFFF - NSActivityUserInitiatedAllowingIdleSystemSleep = ( - NSActivityUserInitiated & - ~NSActivityIdleSystemSleepDisabled - ) - - info = NSProcessInfo.processInfo() - activity = info.beginActivityWithOptions_reason_( - NSActivityUserInitiatedAllowingIdleSystemSleep, - "because reasons" - ) - return activity -