Repository: spyder-ide/spyder

Issue:
Execute in dedicated console without clearing variables
<!--- **PLEASE READ:** When submitting here, please ensure you've completed the following checklist and checked the boxes to confirm. Issue reports without it may be closed. Thanks! --->

### Issue Report Checklist

* [x] Searched the [issues page](https://github.com/spyder-ide/spyder/issues?q=is%3Aissue) for similar reports
* [x] Read the relevant sections of the [Spyder Troubleshooting Guide](https://github.com/spyder-ide/spyder/wiki/Troubleshooting-Guide-and-FAQ) and followed its advice
* [x] Reproduced the issue after updating with ``conda update spyder`` (or ``pip``, if not using Anaconda)
* [x] Could not reproduce inside ``jupyter qtconsole`` (if console-related)
* [x] Tried basic troubleshooting (if a bug/error)
    * [x] Restarted Spyder
    * [x] Reset preferences with ``spyder --reset``
    * [x] Reinstalled the latest version of [Anaconda](https://www.anaconda.com/download/)
    * [x] Tried the other applicable steps from the Troubleshooting Guide
* [x] Completed the **Problem Description**, **Steps to Reproduce** and **Version** sections below


## Problem Description

Currently the "Execute in dedicated console" option currently clears the variables in the dedicated console regardless of the value "Remove all variables before execution" check box. As far as I can see it's been this way for at least 5 years.

Tested on latest master, 5.3.3, 5.1.5 and 5.2.x all affected.

I have a fix that I can commit. 

### What steps reproduce the problem?

1. Run a script in a dedicated console using the Run configuration per file" dialog with the "Execute in dedicated console" enabled
2. make sure that the "Remove all variables before execution" option is unchecked
3. After execution run the file again and notice that variables are reset and the your interactive plot windows are closed

### What is the expected output? What do you see instead?
If I don't check the "Remove all variables before execution" option, I expect them to not be removed.


## Versions

* Spyder version: 6.0.0.dev0 702f89d0b (conda)
* Python version: 3.10.6 64-bit
* Qt version: 5.15.6
* PyQt5 version: 5.15.7
* Operating System: Linux 5.15.0-46-generic

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

```# Mandatory:
atomicwrites >=1.2.0          :  1.4.1 (OK)
chardet >=2.0.0               :  5.0.0 (OK)
cloudpickle >=0.5.0           :  2.2.0 (OK)
cookiecutter >=1.6.0          :  2.1.1 (OK)
diff_match_patch >=20181111   :  20200713 (OK)
intervaltree >=3.0.2          :  3.0.2 (OK)
IPython >=7.31.1;<8.0.0       :  7.33.0 (OK)
jedi >=0.17.2;<0.19.0         :  0.18.1 (OK)
jellyfish >=0.7               :  0.9.0 (OK)
jsonschema >=3.2.0            :  4.17.0 (OK)
keyring >=17.0.0              :  23.9.3 (OK)
nbconvert >=4.0               :  7.2.3 (OK)
numpydoc >=0.6.0              :  1.5.0 (OK)
parso >=0.7.0;<0.9.0          :  0.8.3 (OK)
pexpect >=4.4.0               :  4.8.0 (OK)
pickleshare >=0.4             :  0.7.5 (OK)
psutil >=5.3                  :  5.9.3 (OK)
pygments >=2.0                :  2.13.0 (OK)
pylint >=2.5.0;<3.0           :  2.15.5 (OK)
pylint_venv >=2.1.1           :  None (NOK)
pyls_spyder >=0.4.0           :  0.4.0 (OK)
pylsp >=1.5.0;<1.6.0          :  1.5.0 (OK)
pylsp_black >=1.2.0           :  1.2.1 (OK)
qdarkstyle >=3.0.2;<3.1.0     :  3.0.3 (OK)
qstylizer >=0.2.2             :  0.2.2 (OK)
qtawesome >=1.2.1             :  1.2.1 (OK)
qtconsole >=5.3.2;<5.4.0      :  5.4.0.dev0 (OK)
qtpy >=2.1.0                  :  2.2.1 (OK)
rtree >=0.9.7                 :  1.0.1 (OK)
setuptools >=49.6.0           :  65.5.0 (OK)
sphinx >=0.6.6                :  5.3.0 (OK)
spyder_kernels >=2.3.3;<2.4.0 :  3.0.0.dev0 (OK)
textdistance >=4.2.0          :  4.5.0 (OK)
three_merge >=0.1.1           :  0.1.1 (OK)
watchdog >=0.10.3             :  2.1.9 (OK)
xdg >=0.26                    :  0.28 (OK)
zmq >=22.1.0                  :  24.0.1 (OK)

# Optional:
cython >=0.21                 :  0.29.32 (OK)
matplotlib >=3.0.0            :  3.6.2 (OK)
numpy >=1.7                   :  1.23.4 (OK)
pandas >=1.1.1                :  1.5.1 (OK)
scipy >=0.17.0                :  1.9.3 (OK)
sympy >=0.7.3                 :  1.11.1 (OK)
```



Discussion:

Resolving patch:
diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py
--- a/spyder/plugins/ipythonconsole/widgets/main_widget.py
+++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py
@@ -2488,7 +2488,8 @@ def execute_code(self, lines, current_client=True, clear_variables=False,
                     sw.sig_prompt_ready.disconnect()
                 except TypeError:
                     pass
-                sw.reset_namespace(warning=False)
+                if clear_variables:
+                    sw.reset_namespace(warning=False)
             elif current_client and clear_variables:
                 sw.reset_namespace(warning=False)
 


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
@@ -733,8 +733,10 @@ def test_dedicated_consoles(main_window, qtbot):
 
     # --- Set run options for this file ---
     rc = RunConfiguration().get()
+    
     # A dedicated console is used when these three options are False
     rc['default'] = rc['current'] = rc['systerm'] = False
+    rc['clear_namespace'] = False
     config_entry = (test_file, rc)
     CONF.set('run', 'configurations', [config_entry])
 
@@ -759,10 +761,23 @@ def test_dedicated_consoles(main_window, qtbot):
     text = control.toPlainText()
     assert ('runfile' in text) and not ('Python' in text or 'IPython' in text)
 
-    # --- Clean namespace after re-execution ---
+    # --- Check namespace retention after re-execution ---
     with qtbot.waitSignal(shell.executed):
         shell.execute('zz = -1')
 
+    qtbot.keyClick(code_editor, Qt.Key_F5)
+    qtbot.waitUntil(lambda: shell.is_defined('zz'))
+    assert shell.is_defined('zz')
+
+    # --- Assert runfile text is present after reruns ---
+    assert 'runfile' in control.toPlainText()
+
+    # --- Clean namespace after re-execution with clear_namespace ---
+    rc['clear_namespace'] = True
+    config_entry = (test_file, rc)
+    CONF.set('run', 'configurations', [config_entry])
+
+    qtbot.wait(500)
     qtbot.keyClick(code_editor, Qt.Key_F5)
     qtbot.waitUntil(lambda: not shell.is_defined('zz'))
     assert not shell.is_defined('zz')