Repository: spyder-ide/spyder

Issue:
TypeError: 'method' object is not connected when running from the Files pane
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

The console of the spyder crashes when I try to select and run more than one file from Files Section

### Traceback
```python-traceback
  File "C:\Users\Hub\AppData\Local\Programs\Spyder\pkgs\spyder\plugins\ipythonconsole\widgets\client.py", line 346, in _when_prompt_is_ready
    self._when_prompt_is_ready)
TypeError: 'method' object is not connected
```

## Versions

* Spyder version: 4.2.1 
* Python version: 3.7.9
* Qt version: 5.12.10
* PyQt5 version: 5.12.3
* Operating System: Windows 10

### Dependencies

```

# Mandatory:
atomicwrites >=1.2.0            :  1.4.0 (OK)
chardet >=2.0.0                 :  4.0.0 (OK)
cloudpickle >=0.5.0             :  1.6.0 (OK)
diff_match_patch >=20181111     :  20200713 (OK)
intervaltree                    :  None (OK)
IPython >=7.6.0                 :  7.19.0 (OK)
jedi =0.17.2                    :  0.17.2 (OK)
jsonschema >=3.2.0              :  3.2.0 (OK)
keyring >=17.0.0                :  21.5.0 (OK)
nbconvert >=4.0                 :  6.0.7 (OK)
numpydoc >=0.6.0                :  1.1.0 (OK)
paramiko >=2.4.0                :  2.7.2 (OK)
parso =0.7.0                    :  0.7.0 (OK)
pexpect >=4.4.0                 :  4.8.0 (OK)
pickleshare >=0.4               :  0.7.5 (OK)
psutil >=5.3                    :  5.7.3 (OK)
pygments >=2.0                  :  2.7.3 (OK)
pylint >=1.0                    :  2.6.0 (OK)
pyls >=0.36.2;<1.0.0            :  0.36.2 (OK)
pyls_black >=0.4.6              :  0.4.6 (OK)
pyls_spyder >=0.3.0             :  0.3.0 (OK)
qdarkstyle >=2.8                :  2.8.1 (OK)
qtawesome >=0.5.7               :  1.0.2 (OK)
qtconsole >=5.0.1               :  5.0.1 (OK)
qtpy >=1.5.0                    :  1.9.0 (OK)
rtree >=0.8.3                   :  0.9.4 (OK)
setuptools >=39.0.0             :  51.0.0 (OK)
sphinx >=0.6.6                  :  3.3.1 (OK)
spyder_kernels >=1.10.1;<1.11.0 :  1.10.1 (OK)
textdistance >=4.2.0            :  4.2.0 (OK)
three_merge >=0.1.1             :  0.1.1 (OK)
watchdog                        :  1.0.2 (OK)
zmq >=17                        :  20.0.0 (OK)

# Optional:
cython >=0.21                   :  0.29.21 (OK)
matplotlib >=2.0.0              :  3.3.3 (OK)
numpy >=1.7                     :  1.19.3 (OK)
pandas >=1.1.1                  :  1.1.5 (OK)
scipy >=0.17.0                  :  1.5.4 (OK)
sympy >=0.7.3                   :  1.7.1 (OK)
```



Discussion:
Hi @thamilthedal thanks for reporting this. I can successfully reproduce this bug. When I select two files in the Files pane and press the "Run" button in the context menu I get the following error dialog.
![image](https://user-images.githubusercontent.com/18587879/115941603-a44e8f80-a46b-11eb-89e1-38d7955a738b.png)

We will fix this in our next version of Spyder. 

Thanks. Keep up the great work. 😃

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
@@ -1509,6 +1509,7 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False,
         if client.shellwidget.kernel_manager is None:
             return
         self.register_client(client, give_focus=give_focus)
+        return client
 
     def create_client_for_kernel(self, connection_file, hostname, sshkey,
                                  password):
@@ -1808,17 +1809,19 @@ def create_client_from_path(self, path):
     def create_client_for_file(self, filename, is_cython=False):
         """Create a client to execute code related to a file."""
         # Create client
-        self.create_new_client(filename=filename, is_cython=is_cython)
+        client = self.create_new_client(
+            filename=filename, is_cython=is_cython)
 
         # Don't increase the count of master clients
         self.master_clients -= 1
 
         # Rename client tab with filename
-        client = self.get_current_client()
         client.allow_rename = False
         tab_text = self.disambiguate_fname(filename)
         self.rename_client_tab(client, tab_text)
 
+        return client
+
     def get_client_for_file(self, filename):
         """Get client associated with a given file."""
         client = None
@@ -2323,8 +2326,8 @@ def run_script(self, filename, wdir, args, debug, post_mortem,
         else:
             client = self.get_client_for_file(filename)
             if client is None:
-                self.create_client_for_file(filename, is_cython=is_cython)
-                client = self.get_current_client()
+                client = self.create_client_for_file(
+                    filename, is_cython=is_cython)
                 is_new_client = True
 
         if client is not None:
@@ -2370,7 +2373,8 @@ def run_script(self, filename, wdir, args, debug, post_mortem,
                     client.shellwidget.sig_prompt_ready.connect(
                         lambda: self.execute_code(
                             line, current_client, clear_variables,
-                            set_focus=not focus_to_editor
+                            set_focus=not focus_to_editor,
+                            shellwidget=client.shellwidget
                         )
                     )
             except AttributeError:
@@ -2459,9 +2463,12 @@ def update_active_project_path(self, active_project_path):
 
     # ---- For execution
     def execute_code(self, lines, current_client=True, clear_variables=False,
-                     set_focus=True):
+                     set_focus=True, shellwidget=None):
         """Execute code instructions."""
-        sw = self.get_current_shellwidget()
+        if current_client:
+            sw = self.get_current_shellwidget()
+        else:
+            sw = shellwidget
         if sw is not None:
             if not current_client:
                 # Clear console and reset namespace for


Test patch:
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
@@ -2386,5 +2386,58 @@ def test_varexp_magic_dbg_locals(ipyconsole, qtbot):
     assert shell._control.toHtml().count('img src') == 1
 
 
+def test_run_script(ipyconsole, qtbot, tmp_path):
+    """
+    Test running multiple scripts at the same time.
+
+    This is a regression test for issue spyder-ide/spyder#15405
+    """
+    # Wait until the window is fully up
+    shell = ipyconsole.get_current_shellwidget()
+    qtbot.waitUntil(
+        lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT)
+
+    # Create two temp files: 'a.py' and 'b.py'
+    dir_a = tmp_path / 'a'
+    dir_a.mkdir()
+    filename_a = dir_a / 'a.py'
+    filename_a.write_text('a = 1')
+
+    dir_b = tmp_path / 'b'
+    dir_b.mkdir()
+    filename_b = dir_a / 'b.py'
+    filename_b.write_text('b = 1')
+
+    filenames = [str(filename_a), str(filename_b)]
+
+    # Run scripts
+    for filename in filenames:
+        ipyconsole.run_script(
+            filename=filename,
+            wdir=osp.dirname(filename),
+            current_client=False,
+            clear_variables=True
+        )
+
+    # Validate created consoles names and code executed
+    for filename in filenames:
+        basename = osp.basename(filename)
+        client_name = f'{basename}/A'
+        variable_name = basename.split('.')[0]
+
+        client = ipyconsole.get_client_for_file(filename)
+        assert client.get_name() == client_name
+
+        sw = client.shellwidget
+        qtbot.waitUntil(
+            lambda: sw._prompt_html is not None, timeout=SHELL_TIMEOUT)
+
+        # Wait for the respective script to be run
+        control = client.get_control()
+        qtbot.waitUntil(
+            lambda: "In [2]:" in control.toPlainText(), timeout=SHELL_TIMEOUT)
+        assert sw.get_value(variable_name) == 1
+
+
 if __name__ == "__main__":
     pytest.main()