Repository: spyder-ide/spyder

Issue:
Variables need to be deleted twice in the variable viewer
## Problem Description

Variables need to be deleted twice in the variable viewer

### What steps reproduce the problem?

![spyder变量需要删除两次](https://user-images.githubusercontent.com/1759365/81521941-5d12e000-937b-11ea-9ffc-8ed4fd611626.gif)

## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version: 4.1.3
* Python version: 3.7.5
* Qt version: 5.9.6
* PyQt version: 5.9.2
* Operating System name/version:win 10 



Discussion:

Resolving patch:
diff --git a/spyder/plugins/ipythonconsole/widgets/shell.py b/spyder/plugins/ipythonconsole/widgets/shell.py
--- a/spyder/plugins/ipythonconsole/widgets/shell.py
+++ b/spyder/plugins/ipythonconsole/widgets/shell.py
@@ -19,7 +19,7 @@
 from qtpy.QtWidgets import QMessageBox
 
 # Local imports
-from spyder.config.base import _
+from spyder.config.base import _, running_under_pytest
 from spyder.config.manager import CONF
 from spyder.py3compat import to_text_string
 from spyder.utils import programs, encoding
@@ -345,6 +345,11 @@ def reset_namespace(self, warning=False, message=False):
         reset_str = _("Remove all variables")
         warn_str = _("All user-defined variables will be removed. "
                      "Are you sure you want to proceed?")
+
+        # Don't show the warning when running our tests.
+        if running_under_pytest():
+            warning = False
+
         # This is necessary to make resetting variables work in external
         # kernels.
         # See spyder-ide/spyder#9505.
@@ -397,7 +402,11 @@ def reset_namespace(self, warning=False, message=False):
                     self.silent_execute(dedent(sympy_init))
                 if kernel_env.get('SPY_RUN_CYTHON') == 'True':
                     self.silent_execute("%reload_ext Cython")
-                self.refresh_namespacebrowser()
+
+                # This doesn't need to interrupt the kernel because
+                # "%reset -f" is being executed before it.
+                # Fixes spyder-ide/spyder#12689
+                self.refresh_namespacebrowser(interrupt=False)
 
                 if not self.external_kernel:
                     self.call_kernel().close_all_mpl_figures()


Test patch:
diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py
--- a/spyder/app/tests/test_mainwindow.py
+++ b/spyder/app/tests/test_mainwindow.py
@@ -3286,10 +3286,11 @@ def test_varexp_cleared_after_kernel_restart(main_window, qtbot):
     qtbot.waitUntil(lambda: shell._prompt_html is not None,
                     timeout=SHELL_TIMEOUT)
 
+    # Create a variable
     with qtbot.waitSignal(shell.executed):
         shell.execute('a = 10')
 
-    # Assert the value was created
+    # Assert the value is shown in the variable explorer
     nsb = main_window.variableexplorer.get_focus_widget()
     qtbot.waitUntil(lambda: 'a' in nsb.editor.source_model._data,
                     timeout=3000)
@@ -3303,5 +3304,50 @@ def test_varexp_cleared_after_kernel_restart(main_window, qtbot):
                     timeout=3000)
 
 
+@pytest.mark.slow
+@flaky(max_runs=3)
+def test_varexp_cleared_after_pressing_reset_button(main_window, qtbot):
+    """
+    Test that the variable explorer is cleared after triggering a
+    in the IPython console and variable explorer panes.
+    """
+    shell = main_window.ipyconsole.get_current_shellwidget()
+    control = main_window.ipyconsole.get_focus_widget()
+    qtbot.waitUntil(lambda: shell._prompt_html is not None,
+                    timeout=SHELL_TIMEOUT)
+
+    # Create a variable
+    with qtbot.waitSignal(shell.executed):
+        shell.execute('a = 10')
+
+    # Assert the value is shown in the variable explorer
+    nsb = main_window.variableexplorer.get_focus_widget()
+    qtbot.waitUntil(lambda: 'a' in nsb.editor.source_model._data,
+                    timeout=3000)
+
+    # Trigger a reset in the variable explorer
+    nsb.reset_namespace()
+
+    # Assert the value was removed
+    qtbot.waitUntil(lambda: 'a' not in nsb.editor.source_model._data,
+                    timeout=3000)
+
+    # Create the variable again
+    with qtbot.waitSignal(shell.executed):
+        shell.execute('a = 10')
+
+    # Assert the value is shown in the variable explorer
+    nsb = main_window.variableexplorer.get_focus_widget()
+    qtbot.waitUntil(lambda: 'a' in nsb.editor.source_model._data,
+                    timeout=3000)
+
+    # Trigger a reset in the console
+    shell.ipyclient.reset_namespace()
+
+    # Assert the value was removed
+    qtbot.waitUntil(lambda: 'a' not in nsb.editor.source_model._data,
+                    timeout=3000)
+
+
 if __name__ == "__main__":
     pytest.main()
diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
--- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
+++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
@@ -320,7 +320,7 @@ def test_pylab_client(ipyconsole, qtbot):
     assert 'Error' not in control.toPlainText()
 
     # Reset the console namespace
-    shell.reset_namespace(warning=False)
+    shell.reset_namespace()
     qtbot.wait(1000)
 
     # See that `e` is still defined from numpy after reset
@@ -352,7 +352,7 @@ def test_sympy_client(ipyconsole, qtbot):
     assert 'NameError' not in control.toPlainText()
 
     # Reset the console namespace
-    shell.reset_namespace(warning=False)
+    shell.reset_namespace()
     qtbot.wait(1000)
 
     # See that `e` is still defined from sympy after reset
@@ -388,7 +388,7 @@ def test_cython_client(ipyconsole, qtbot):
     assert 'Error' not in control.toPlainText()
 
     # Reset the console namespace
-    shell.reset_namespace(warning=False)
+    shell.reset_namespace()
     qtbot.wait(1000)
 
     # See that cython is still enabled after reset
@@ -1086,7 +1086,7 @@ def test_clear_and_reset_magics_dbg(ipyconsole, qtbot):
     qtbot.waitUntil(lambda: control.toPlainText().split()[-1] == 'ipdb>')
     assert shell.get_value('bb') == 10
 
-    shell.reset_namespace(warning=False)
+    shell.reset_namespace()
     qtbot.wait(1000)
 
     qtbot.keyClicks(control, '!bb')