We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AttributeError: 'BlockingKernelClient' object has no attribute 'started_channels'
I have an app that is centered around IPython running in qt (https://github.com/neuropy/neuropy/blob/master/neuropy/main.py). Yesterday I updated IPython from git, and have had to change a few things since the refactoring about 3 months ago, which split a kernel client off of the kernel manager (see #3011). In the process, I came across the error in the title. This error only happens when I try to create a normal two-process app, but not when creating an inprocess app, ie, it happens with QtKernelManager, but not with QtInProcessKernelManager. I think a BlockingKernelClient happens to be the default for QtKernelClient, which may be due to #3222. Not sure how to fix this in IPython, but to get around it, I set the client_factory to QtKernelClient, and then calling QtKernelManager.client() works fine. My minimalist example is in this gist: https://gist.github.com/mspacek/6018206

Can this be fixed so that setting up the 2-process app doesn't require the extra step of changing the client_factory?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `IPython/qt/manager.py`
Content:
```
1 """ Defines a KernelClient that provides signals and slots.
2 """
3 
4 from IPython.external.qt import QtCore
5 
6 # Local imports
7 from IPython.utils.traitlets import Bool
8 
9 from IPython.kernel import KernelManager
10 from IPython.kernel.restarter import KernelRestarter
11 
12 from .kernel_mixins import QtKernelManagerMixin, QtKernelRestarterMixin
13 
14 
15 class QtKernelRestarter(KernelRestarter, QtKernelRestarterMixin):
16 
17     def start(self):
18         if self._timer is None:
19             self._timer = QtCore.QTimer()
20             self._timer.timeout.connect(self.poll)
21         self._timer.start(self.time_to_dead * 1000)
22 
23     def stop(self):
24         self._timer.stop()
25 
26     def poll(self):
27         super(QtKernelRestarter, self).poll()
28 
29 
30 class QtKernelManager(KernelManager, QtKernelManagerMixin):
31     """A KernelManager with Qt signals for restart"""
32 
33     autorestart = Bool(True, config=True)
34 
35     def start_restarter(self):
36         if self.autorestart and self.has_kernel:
37             if self._restarter is None:
38                 self._restarter = QtKernelRestarter(
39                     kernel_manager=self,
40                     parent=self,
41                     log=self.log,
42                 )
43                 self._restarter.add_callback(self._handle_kernel_restarted)
44             self._restarter.start()
45 
46     def stop_restarter(self):
47         if self.autorestart:
48             if self._restarter is not None:
49                 self._restarter.stop()
50 
51     def _handle_kernel_restarted(self):
52         self.kernel_restarted.emit()
53
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/IPython/qt/manager.py b/IPython/qt/manager.py
--- a/IPython/qt/manager.py
+++ b/IPython/qt/manager.py
@@ -4,7 +4,7 @@
 from IPython.external.qt import QtCore
 
 # Local imports
-from IPython.utils.traitlets import Bool
+from IPython.utils.traitlets import Bool, DottedObjectName
 
 from IPython.kernel import KernelManager
 from IPython.kernel.restarter import KernelRestarter
@@ -30,6 +30,7 @@
 class QtKernelManager(KernelManager, QtKernelManagerMixin):
     """A KernelManager with Qt signals for restart"""
 
+    client_class = DottedObjectName('IPython.qt.client.QtKernelClient')
     autorestart = Bool(True, config=True)
 
     def start_restarter(self):

{"golden_diff": "diff --git a/IPython/qt/manager.py b/IPython/qt/manager.py\n--- a/IPython/qt/manager.py\n+++ b/IPython/qt/manager.py\n@@ -4,7 +4,7 @@\n from IPython.external.qt import QtCore\n \n # Local imports\n-from IPython.utils.traitlets import Bool\n+from IPython.utils.traitlets import Bool, DottedObjectName\n \n from IPython.kernel import KernelManager\n from IPython.kernel.restarter import KernelRestarter\n@@ -30,6 +30,7 @@\n class QtKernelManager(KernelManager, QtKernelManagerMixin):\n     \"\"\"A KernelManager with Qt signals for restart\"\"\"\n \n+    client_class = DottedObjectName('IPython.qt.client.QtKernelClient')\n     autorestart = Bool(True, config=True)\n \n     def start_restarter(self):\n", "issue": "AttributeError: 'BlockingKernelClient' object has no attribute 'started_channels'\nI have an app that is centered around IPython running in qt (https://github.com/neuropy/neuropy/blob/master/neuropy/main.py). Yesterday I updated IPython from git, and have had to change a few things since the refactoring about 3 months ago, which split a kernel client off of the kernel manager (see #3011). In the process, I came across the error in the title. This error only happens when I try to create a normal two-process app, but not when creating an inprocess app, ie, it happens with QtKernelManager, but not with QtInProcessKernelManager. I think a BlockingKernelClient happens to be the default for QtKernelClient, which may be due to #3222. Not sure how to fix this in IPython, but to get around it, I set the client_factory to QtKernelClient, and then calling QtKernelManager.client() works fine. My minimalist example is in this gist: https://gist.github.com/mspacek/6018206\n\nCan this be fixed so that setting up the 2-process app doesn't require the extra step of changing the client_factory?\n\n", "before_files": [{"content": "\"\"\" Defines a KernelClient that provides signals and slots.\n\"\"\"\n\nfrom IPython.external.qt import QtCore\n\n# Local imports\nfrom IPython.utils.traitlets import Bool\n\nfrom IPython.kernel import KernelManager\nfrom IPython.kernel.restarter import KernelRestarter\n\nfrom .kernel_mixins import QtKernelManagerMixin, QtKernelRestarterMixin\n\n\nclass QtKernelRestarter(KernelRestarter, QtKernelRestarterMixin):\n\n    def start(self):\n        if self._timer is None:\n            self._timer = QtCore.QTimer()\n            self._timer.timeout.connect(self.poll)\n        self._timer.start(self.time_to_dead * 1000)\n\n    def stop(self):\n        self._timer.stop()\n\n    def poll(self):\n        super(QtKernelRestarter, self).poll()\n\n\nclass QtKernelManager(KernelManager, QtKernelManagerMixin):\n    \"\"\"A KernelManager with Qt signals for restart\"\"\"\n\n    autorestart = Bool(True, config=True)\n\n    def start_restarter(self):\n        if self.autorestart and self.has_kernel:\n            if self._restarter is None:\n                self._restarter = QtKernelRestarter(\n                    kernel_manager=self,\n                    parent=self,\n                    log=self.log,\n                )\n                self._restarter.add_callback(self._handle_kernel_restarted)\n            self._restarter.start()\n\n    def stop_restarter(self):\n        if self.autorestart:\n            if self._restarter is not None:\n                self._restarter.stop()\n\n    def _handle_kernel_restarted(self):\n        self.kernel_restarted.emit()\n", "path": "IPython/qt/manager.py"}], "after_files": [{"content": "\"\"\" Defines a KernelClient that provides signals and slots.\n\"\"\"\n\nfrom IPython.external.qt import QtCore\n\n# Local imports\nfrom IPython.utils.traitlets import Bool, DottedObjectName\n\nfrom IPython.kernel import KernelManager\nfrom IPython.kernel.restarter import KernelRestarter\n\nfrom .kernel_mixins import QtKernelManagerMixin, QtKernelRestarterMixin\n\n\nclass QtKernelRestarter(KernelRestarter, QtKernelRestarterMixin):\n\n    def start(self):\n        if self._timer is None:\n            self._timer = QtCore.QTimer()\n            self._timer.timeout.connect(self.poll)\n        self._timer.start(self.time_to_dead * 1000)\n\n    def stop(self):\n        self._timer.stop()\n\n    def poll(self):\n        super(QtKernelRestarter, self).poll()\n\n\nclass QtKernelManager(KernelManager, QtKernelManagerMixin):\n    \"\"\"A KernelManager with Qt signals for restart\"\"\"\n\n    client_class = DottedObjectName('IPython.qt.client.QtKernelClient')\n    autorestart = Bool(True, config=True)\n\n    def start_restarter(self):\n        if self.autorestart and self.has_kernel:\n            if self._restarter is None:\n                self._restarter = QtKernelRestarter(\n                    kernel_manager=self,\n                    parent=self,\n                    log=self.log,\n                )\n                self._restarter.add_callback(self._handle_kernel_restarted)\n            self._restarter.start()\n\n    def stop_restarter(self):\n        if self.autorestart:\n            if self._restarter is not None:\n                self._restarter.stop()\n\n    def _handle_kernel_restarted(self):\n        self.kernel_restarted.emit()\n", "path": "IPython/qt/manager.py"}]}