Repository: spyder-ide/spyder
Pull request: PR: Avoid Python destroying Qt window and causing segfault
<!--- Make sure to read the Contributing Guidelines:                   --->
<!--- https://github.com/spyder-ide/spyder/blob/master/CONTRIBUTING.md --->
<!--- and follow PEP 8, PEP 257 and Spyder's code style:               --->
<!--- https://github.com/spyder-ide/spyder/wiki/Dev:-Coding-Style      --->

## Description of Changes


* [ ] Wrote at least one-line docstrings (for any new functions)
* [ ] Added unit test(s) covering the changes (if testable)
<!--- Remember that an image/animation is worth a thousand words! --->
* [ ] Included a screenshot or animation (if affecting the UI, see [Licecap](https://www.cockos.com/licecap/))


<!--- Explain what you've done and why --->
Keep a reference to the window being closed when redocking a widget to avoid a segfault.



### Issue(s) Resolved

<!--- List the issue(s) below, in the form "Fixes #1234"; one per line --->

Fixes #10704


### Affirmation

By submitting this Pull Request or typing my (user)name below,
I affirm the [Developer Certificate of Origin](https://developercertificate.org)
with respect to all commits and content included in this PR,
and understand I am releasing the same under Spyder's MIT (Expat) license.

<!--- TYPE YOUR USER/NAME AFTER THE FOLLOWING: --->
I certify the above statement is true and correct:

<!--- Thanks for your help making Spyder better for everyone! --->


Problem statement:
Segmentation fault when closing an undocked plugin
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

The crash happened while trying to close an undocked plugin (Outline explorer)

### Traceback
C traceback
```
Thread 0 Crashed:: ZMQbg/5  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	0x00007fff6ffdcb66 __pthread_kill + 10
1   libsystem_pthread.dylib       	0x00007fff701a7080 pthread_kill + 333
2   libsystem_c.dylib             	0x00007fff6feea6fe raise + 26
3   libsystem_platform.dylib      	0x00007fff7019af5a _sigtramp + 26
4   ???                           	000000000000000000 0 + 0
5   libqcocoa.dylib               	0x000000012aa1a316 0x12aa00000 + 107286
6   QtGui                         	0x00000001204a4bd2 QWindowPrivate::setVisible(bool) + 1154
7   QtWidgets                     	0x000000011ff0e266 QWidgetPrivate::hide_sys() + 214
8   QtWidgets                     	0x000000011ff10bf7 QWidgetPrivate::hide_helper() + 167
9   QtWidgets                     	0x000000011ff110d5 QWidgetPrivate::setVisible(bool) + 373
10  QtWidgets.so                  	0x000000011f9f93ae sipQMainWindow::setVisible(bool) + 110
11  QtWidgets                     	0x000000011ff00841 QWidgetPrivate::close_helper(QWidgetPrivate::CloseMode) + 273
12  QtWidgets                     	0x000000011ff2f900 0x11fec5000 + 436480
13  QtWidgets                     	0x000000011fed576d QApplicationPrivate::notify_helper(QObject*, QEvent*) + 269
14  QtWidgets                     	0x000000011fed6b72 QApplication::notify(QObject*, QEvent*) + 594
15  QtWidgets.so                  	0x000000011faa43ca sipQApplication::notify(QObject*, QEvent*) + 234
16  QtCore                        	0x000000011f14b7c4 QCoreApplication::notifyInternal2(QObject*, QEvent*) + 212
17  QtGui                         	0x00000001204997df QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent*) + 2463
18  QtGui                         	0x000000012048160b QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 219
19  QtGui                         	0x000000012047c545 QWindowSystemInterface::flushWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 581
20  libqcocoa.dylib               	0x000000012aa1e5dd 0x12aa00000 + 124381
21  libqcocoa.dylib               	0x000000012aa33175 0x12aa00000 + 209269
22  com.apple.AppKit              	0x00007fff457bc5a1 __19-[NSWindow __close]_block_invoke + 185
23  com.apple.AppKit              	0x00007fff457bc4db -[NSWindow __close] + 297
24  com.apple.AppKit              	0x00007fff45c61a43 -[NSApplication(NSResponder) sendAction:to:from:] + 312
25  com.apple.AppKit              	0x00007fff4570753f -[NSControl sendAction:to:] + 86
26  com.apple.AppKit              	0x00007fff45707467 __26-[NSCell _sendActionFrom:]_block_invoke + 136
27  com.apple.AppKit              	0x00007fff4570736d -[NSCell _sendActionFrom:] + 183
28  com.apple.AppKit              	0x00007fff45748688 -[NSButtonCell _sendActionFrom:] + 97
29  com.apple.AppKit              	0x00007fff45705bd6 -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 2438
30  com.apple.AppKit              	0x00007fff457483cf -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 777
31  com.apple.AppKit              	0x00007fff45704670 -[NSControl mouseDown:] + 965
32  com.apple.AppKit              	0x00007fff4579ebd9 -[_NSThemeWidget mouseDown:] + 86
33  com.apple.AppKit              	0x00007fff45e00d6d -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 5891
34  com.apple.AppKit              	0x00007fff45dfd9c4 -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 2359
35  com.apple.AppKit              	0x00007fff45dfcc70 -[NSWindow(NSEventRouting) sendEvent:] + 497
36  libqcocoa.dylib               	0x000000012aa31b25 0x12aa00000 + 203557
37  com.apple.AppKit              	0x00007fff45c5e236 -[NSApplication(NSEvent) sendEvent:] + 2462
38  libqcocoa.dylib               	0x000000012aa38b7a 0x12aa00000 + 232314
39  com.apple.AppKit              	0x00007fff454be87d -[NSApplication run] + 812
40  libqcocoa.dylib               	0x000000012aa35253 0x12aa00000 + 217683
41  QtCore                        	0x000000011f146ddf QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 431
42  QtCore                        	0x000000011f14bdd2 QCoreApplication::exec() + 130
43  QtWidgets.so                  	0x000000011fb937d2 meth_QApplication_exec_(_object*, _object*) + 82
44  python3                       	0x000000010ca107ce _PyMethodDef_RawFastCallKeywords + 350
45  python3                       	0x000000010cb4cc82 call_function + 306
46  python3                       	0x000000010cb49a36 _PyEval_EvalFrameDefault + 42246
47  python3                       	0x000000010ca0fef5 function_code_fastcall + 117
48  python3                       	0x000000010cb4cc07 call_function + 183
49  python3                       	0x000000010cb4a978 _PyEval_EvalFrameDefault + 46152
50  python3                       	0x000000010ca0fef5 function_code_fastcall + 117
51  python3                       	0x000000010cb4cc07 call_function + 183
52  python3                       	0x000000010cb49a36 _PyEval_EvalFrameDefault + 42246
53  python3                       	0x000000010ca0fef5 function_code_fastcall + 117
54  python3                       	0x000000010cb4cc07 call_function + 183
55  python3                       	0x000000010cb49a36 _PyEval_EvalFrameDefault + 42246
56  python3                       	0x000000010cb3e2ee _PyEval_EvalCodeWithName + 414
57  python3                       	0x000000010cba1b30 PyRun_FileExFlags + 256
58  python3                       	0x000000010cba0fa7 PyRun_SimpleFileExFlags + 391
59  python3                       	0x000000010cbcec03 pymain_main + 9635
60  python3                       	0x000000010c9e2b4d main + 125
61  libdyld.dylib                 	0x00007fff6fe8c015 start + 1
```
```python-traceback
Fatal Python error: Segmentation fault

Thread 0x0000700016c97000 (most recent call first):
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 300 in wait
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 552 in wait
  File "/Users/quentin/opt/anaconda3/lib/python3.7/site-packages/jupyter_client/channels.py", line 159 in run
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 926 in _bootstrap_inner
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 890 in _bootstrap

Thread 0x000070001668e000 (most recent call first):
  File "/Users/quentin/opt/anaconda3/lib/python3.7/selectors.py", line 558 in select
  File "/Users/quentin/opt/anaconda3/lib/python3.7/asyncio/base_events.py", line 1735 in _run_once
  File "/Users/quentin/opt/anaconda3/lib/python3.7/asyncio/base_events.py", line 534 in run_forever
  File "/Users/quentin/opt/anaconda3/lib/python3.7/site-packages/tornado/platform/asyncio.py", line 148 in start
  File "/Users/quentin/opt/anaconda3/lib/python3.7/site-packages/jupyter_client/threaded.py", line 188 in run
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 926 in _bootstrap_inner
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 890 in _bootstrap

Thread 0x0000700016085000 (most recent call first):
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 296 in wait
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 552 in wait
  File "/Users/quentin/opt/anaconda3/lib/python3.7/site-packages/IPython/core/history.py", line 829 in run
  File "/Users/quentin/opt/anaconda3/lib/python3.7/site-packages/IPython/core/history.py", line 58 in needs_sqlite
  File "</Users/quentin/opt/anaconda3/lib/python3.7/site-packages/decorator.py:decorator-gen-25>", line 2 in run
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 926 in _bootstrap_inner
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 890 in _bootstrap

Thread 0x0000700015b82000 (most recent call first):
  File "/Users/quentin/opt/anaconda3/lib/python3.7/socket.py", line 212 in accept
  File "/Users/quentin/pyscript/spyder/spyder/app/mainwindow.py", line 3248 in start_open_files_server
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 870 in run
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 926 in _bootstrap_inner
  File "/Users/quentin/opt/anaconda3/lib/python3.7/threading.py", line 890 in _bootstrap

Current thread 0x00007fffa88c4380 (most recent call first):
  File "/Users/quentin/pyscript/spyder/spyder/app/mainwindow.py", line 3619 in run_spyder
  File "/Users/quentin/pyscript/spyder/spyder/app/mainwindow.py", line 3720 in main
  File "/Users/quentin/pyscript/spyder/spyder/app/start.py", line 204 in main
  File "bootstrap.py", line 186 in <module>
```

## Versions

* Spyder version: 4.0.0.dev0 fdb4b37a8
* Python version: 3.7.5
* Qt version: 5.12.4
* PyQt5 version: 5.12.3
* Operating System: Darwin 17.7.0

### Dependencies

```
cloudpickle >=0.5.0          :  1.2.2 (OK)
pygments >=2.0               :  2.4.2 (OK)
qtconsole >=4.5.5            :  4.5.5 (OK)
nbconvert >=4.0              :  5.6.1 (OK)
sphinx >=0.6.6               :  2.2.1 (OK)
pylint >=0.25                :  2.4.3 (OK)
psutil >=0.3                 :  5.6.3 (OK)
qtawesome >=0.5.7            :  0.6.0 (OK)
qtpy >=1.5.0                 :  1.9.0 (OK)
pickleshare >=0.4            :  0.7.5 (OK)
zmq >=17                     :  18.1.0 (OK)
chardet >=2.0.0              :  3.0.4 (OK)
numpydoc >=0.6.0             :  0.9.1 (OK)
spyder_kernels >=1.7.0;<2.0.0:  1.8.0.dev0 (OK)
qdarkstyle >=2.7             :  2.7 (OK)
atomicwrites >=1.2.0         :  1.3.0 (OK)
diff_match_patch >=20181111  :  20181111 (OK)
watchdog                     :  None (OK)
keyring                      :  None (OK)
pexpect >=4.4.0              :  4.7.0 (OK)
pympler                      :  None (OK)
sympy >=0.7.3                :  1.4 (OK)
cython >=0.21                :  0.29.13 (OK)
IPython >=4.0                :  7.9.0 (OK)
matplotlib >=2.0.0           :  3.1.1 (OK)
pandas >=0.13.1              :  0.25.2 (OK)
numpy >=1.7                  :  1.17.3 (OK)
scipy >=0.17.0               :  1.3.1 (OK)
pyls >=0.29.3;<0.30.0        :  0.28.3 (NOK)
applaunchservices >=0.1.7    :  0.2.1 (OK)
rtree >=0.8.3                :  0.8.3 (OK)
```

Hints from issue discussion:
@goanpeca, are you able to reproduce this one in macOS?
No, closing the outline explorer closes the window and redocks it

<img width="715" alt="Screen Shot 2019-11-25 at 14 27 50" src="https://user-images.githubusercontent.com/3627835/69578711-0c5f2480-0f9f-11ea-9d33-cba5de42e1fb.png">

I could reproduce on two different macs:
![crash](https://user-images.githubusercontent.com/6740194/69610474-ce581400-1023-11ea-8748-48d91d5dfca7.gif)

Oops I pasted the wrong screenshot! Hmmm not sure what might be causing this, but this type of segfaults are usually an unpareted widget 🤷‍♂  that was collected/deleted on  C++ ?
> Oops I pasted the wrong screenshot! Hmmm not sure what might be causing this, but this type of segfaults are usually an unpareted widget 🤷‍♂ that was collected/deleted on C++ ?

I am not sure about that, It looks like the code is not called from python, so I don't think this is caused by an empty wrapper.
I can't reproduce this on Linux with PyQt 5.9

Commit message: Avoid python destroying qt object

Patch to review:
diff --git a/spyder/plugins/base.py b/spyder/plugins/base.py
index cf1e5bf37f4..599cbcf729a 100644
--- a/spyder/plugins/base.py
+++ b/spyder/plugins/base.py
@@ -136,6 +136,10 @@ def closeEvent(self, event):
         self.plugin.dockwidget.setVisible(True)
         self.plugin.switch_to_plugin()
         QMainWindow.closeEvent(self, event)
+        # Qt might want to do something with this soon,
+        # So it should not be deleted by python yet.
+        # Fixes #10704
+        self.plugin.__unsafe__window = self
         self.plugin._undocked_window = None
 
 


Code review comments:
diff_hunk: @@ -136,6 +136,10 @@ def closeEvent(self, event):
         self.plugin.dockwidget.setVisible(True)
         self.plugin.switch_to_plugin()
         QMainWindow.closeEvent(self, event)
+        # Qt might want to do something with this soon,
+        # So it should not be deleted by python yet.
+        # Fixes #10704
path: spyder/plugins/base.py
text: @user1:
Please change this line to

    Fixes spyder-ide/spyder#10704