Skip to content

Commit 756c941

Browse files
authored
Merge pull request #24269 from mrclary/async-env-var
PR: Get user environment variables asynchronously
2 parents 5aefe93 + 9e22123 commit 756c941

File tree

16 files changed

+421
-216
lines changed

16 files changed

+421
-216
lines changed

spyder/app/tests/test_mainwindow.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ def check_cwd():
812812
sys.platform.startswith("linux") and running_in_ci(),
813813
reason='Fails sometimes on Linux and CIs'
814814
)
815-
@pytest.mark.skipif(sys.platform == "darwin", reason="Fails sometimes on Mac")
815+
@pytest.mark.close_main_window
816816
def test_dedicated_consoles(main_window, qtbot):
817817
"""Test running code in dedicated consoles."""
818818
shell = main_window.ipyconsole.get_current_shellwidget()
@@ -821,7 +821,7 @@ def test_dedicated_consoles(main_window, qtbot):
821821
timeout=SHELL_TIMEOUT,
822822
)
823823

824-
# ---- Load test file ----
824+
# --- Load test file ---
825825
test_file = osp.join(LOCATION, 'script.py')
826826
main_window.editor.load(test_file)
827827
code_editor = main_window.editor.get_focus_widget()
@@ -914,7 +914,13 @@ def test_dedicated_consoles(main_window, qtbot):
914914
# --- Assert runfile text is present after reruns ---
915915
assert 'runfile' in control.toPlainText()
916916

917-
# ---- Closing test file and resetting config ----
917+
# --- Assert no re-execution when already executing
918+
shell.execute("import time; time.sleep(5)") # Execute something long
919+
qtbot.keyClick(code_editor, Qt.Key_F5) # Try to execute while busy
920+
qtbot.wait(6000) # First exectution should be done
921+
assert not shell.is_defined("zz") # Second execution did not occur
922+
923+
# --- Closing test file and resetting config ---
918924
main_window.editor.close_file()
919925
CONF.set('run', 'configurations', {})
920926
CONF.set('run', 'last_used_parameters_per_executor', {})
@@ -1006,6 +1012,7 @@ def test_shell_execution(main_window, qtbot, tmpdir):
10061012
reason="Fails frequently on Mac and CI",
10071013
)
10081014
@pytest.mark.order(after="test_debug_unsaved_function")
1015+
@pytest.mark.close_main_window
10091016
def test_connection_to_external_kernel(main_window, qtbot):
10101017
"""Test that only Spyder kernels are connected to the Variable Explorer."""
10111018
# Test with a generic kernel
@@ -1070,13 +1077,13 @@ def test_connection_to_external_kernel(main_window, qtbot):
10701077
shell.execute('q')
10711078

10721079
# Try quitting the kernels
1073-
shell.execute('quit()')
1074-
python_shell.execute('quit()')
1080+
with qtbot.waitSignal(shell.executed):
1081+
shell.execute('quit()')
1082+
with qtbot.waitSignal(python_shell.executed):
1083+
python_shell.execute('quit()')
10751084

10761085
# Make sure everything quit properly
1077-
qtbot.waitUntil(lambda: not km.is_alive())
10781086
assert not km.is_alive()
1079-
qtbot.waitUntil(lambda: not spykm.is_alive())
10801087
assert not spykm.is_alive()
10811088

10821089
# Close the channels
@@ -1198,6 +1205,7 @@ def test_change_cwd_explorer(main_window, qtbot, tmpdir, test_directory):
11981205
parse(ipy_release.version) == parse('7.11.0')),
11991206
reason="Hard to test on Windows and macOS and fails for IPython 7.11.0")
12001207
@pytest.mark.order(after="test_debug_unsaved_function")
1208+
@pytest.mark.close_main_window
12011209
def test_run_cython_code(main_window, qtbot):
12021210
"""Test all the different ways we have to run Cython code"""
12031211
# Wait until the window is fully up
@@ -1217,6 +1225,7 @@ def test_run_cython_code(main_window, qtbot):
12171225

12181226
# Run file
12191227
qtbot.keyClick(code_editor, Qt.Key_F5)
1228+
qtbot.wait(1500)
12201229

12211230
# Get a reference to the namespace browser widget
12221231
nsb = main_window.variableexplorer.current_widget()
@@ -1240,6 +1249,7 @@ def test_run_cython_code(main_window, qtbot):
12401249

12411250
# Run file
12421251
qtbot.keyClick(code_editor, Qt.Key_F5)
1252+
qtbot.wait(500)
12431253

12441254
# Wait until all objects have appeared in the variable explorer
12451255
qtbot.waitUntil(lambda: nsb.editor.source_model.rowCount() == 1,

spyder/plugins/ipythonconsole/tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ def threads_condition():
372372
show_diff(init_files, files, "files")
373373
raise
374374

375+
window.close()
376+
375377

376378
@pytest.fixture
377379
def mpl_rc_file(tmp_path):

spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,10 @@ def test_kernel_kill(ipyconsole, qtbot, external_interpreter):
13991399
shell = ipyconsole.get_current_shellwidget()
14001400

14011401
# Wait for the restarter to start
1402-
qtbot.wait(3000)
1402+
qtbot.waitUntil(
1403+
lambda: shell._prompt_html is not None, timeout=SHELL_TIMEOUT
1404+
)
1405+
14031406
crash_string = 'import os, signal; os.kill(os.getpid(), signal.SIGTERM)'
14041407

14051408
# Check only one comm is open

spyder/plugins/ipythonconsole/utils/kernelspec.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
SPYDER_KERNELS_CONDA, SPYDER_KERNELS_PIP, SPYDER_KERNELS_VERSION,
3737
SpyderKernelError)
3838
from spyder.utils.conda import conda_version, find_conda, find_pixi
39-
from spyder.utils.environ import clean_env, get_user_environment_variables
39+
from spyder.utils.environ import clean_env
4040
from spyder.utils.misc import get_python_executable
4141
from spyder.utils.programs import (
4242
get_module_version,
@@ -91,15 +91,13 @@ class SpyderKernelSpec(KernelSpec, SpyderConfigurationAccessor):
9191

9292
CONF_SECTION = 'ipython_console'
9393

94-
def __init__(self, path_to_custom_interpreter=None,
95-
**kwargs):
94+
def __init__(self, path_to_custom_interpreter=None, **kwargs):
9695
super().__init__(**kwargs)
9796
self.path_to_custom_interpreter = path_to_custom_interpreter
9897
self.display_name = 'Python 3 (Spyder)'
9998
self.language = 'python3'
10099
self.resource_dir = ''
101-
102-
self.env = get_user_environment_variables()
100+
self._env_vars = {}
103101

104102
@property
105103
def argv(self):
@@ -237,7 +235,8 @@ def argv(self):
237235
def env(self):
238236
"""Env vars for kernels"""
239237
default_interpreter = self.get_conf(
240-
'default', section='main_interpreter')
238+
'default', section='main_interpreter'
239+
)
241240

242241
# Ensure that user environment variables are included, but don't
243242
# override existing environ values

spyder/plugins/ipythonconsole/widgets/client.py

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,15 @@ def __init__(
168168
)
169169
self.infowidget = self.container.infowidget
170170
self.blank_page = self._create_blank_page()
171-
self.loading_page = self._create_loading_page()
172-
# To keep a reference to the page to be displayed
173-
# in infowidget
174-
self.info_page = None
171+
self.kernel_loading_page = self._create_loading_page()
172+
self.env_loading_page = self._create_loading_page(env=True)
173+
174+
if self.is_remote():
175+
# Keep a reference
176+
self.info_page = None
177+
else:
178+
# Initially show environment loading page
179+
self.info_page = self.env_loading_page
175180

176181
# Elapsed time
177182
self.t0 = time.monotonic()
@@ -233,16 +238,22 @@ def _when_kernel_is_ready(self):
233238
if self.give_focus:
234239
self.shellwidget._control.setFocus()
235240

236-
def _create_loading_page(self):
241+
def _create_loading_page(self, env=False):
237242
"""Create html page to show while the kernel is starting"""
238243
loading_template = Template(LOADING)
239244
loading_img = get_image_path('loading_sprites')
240245
if os.name == 'nt':
241246
loading_img = loading_img.replace('\\', '/')
242247
message = _("Connecting to kernel...")
243-
page = loading_template.substitute(css_path=self.css_path,
244-
loading_img=loading_img,
245-
message=message)
248+
249+
if env:
250+
message = _("Retrieving environment variables...")
251+
page = loading_template.substitute(
252+
css_path=self.css_path,
253+
loading_img=loading_img,
254+
message=message
255+
)
256+
246257
return page
247258

248259
def _create_blank_page(self):
@@ -251,16 +262,16 @@ def _create_blank_page(self):
251262
page = loading_template.substitute(css_path=self.css_path)
252263
return page
253264

254-
def _show_loading_page(self):
255-
"""Show animation while the kernel is loading."""
265+
def _show_loading_page(self, page=None):
266+
"""Show animation while loading."""
256267
if self.infowidget is not None:
257268
self.shellwidget.hide()
258269
self.infowidget.show()
259-
self.info_page = self.loading_page
270+
self.info_page = page if page else self.kernel_loading_page
260271
self.set_info_page()
261272

262273
def _hide_loading_page(self):
263-
"""Hide animation shown while the kernel is loading."""
274+
"""Hide animation shown while loading."""
264275
if self.infowidget is not None:
265276
self.infowidget.hide()
266277
self.info_page = self.blank_page
@@ -363,6 +374,10 @@ def connection_file(self):
363374

364375
def connect_kernel(self, kernel_handler, first_connect=True):
365376
"""Connect kernel to client using our handler."""
377+
self._hide_loading_page()
378+
if not self.is_remote():
379+
self._show_loading_page(self.kernel_loading_page)
380+
366381
self.kernel_handler = kernel_handler
367382

368383
# Connect standard streams.
@@ -377,11 +392,6 @@ def connect_kernel(self, kernel_handler, first_connect=True):
377392
kernel_handler.sig_kernel_is_ready.connect(
378393
self._when_kernel_is_ready)
379394

380-
if self.is_remote():
381-
self._hide_loading_page()
382-
else:
383-
self._show_loading_page()
384-
385395
# Actually do the connection
386396
self.shellwidget.connect_kernel(kernel_handler, first_connect)
387397

0 commit comments

Comments
 (0)