From 013e5916f72acf92587f157f3528a799d55e397d Mon Sep 17 00:00:00 2001 From: jsbautista Date: Mon, 23 Jan 2023 02:21:16 -0500 Subject: [PATCH 01/27] Add menu consoles environment --- .../ipythonconsole/widgets/main_widget.py | 127 +++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index c39f6fb4a33..a4cec3d67a9 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -45,6 +45,11 @@ from spyder.widgets.browser import FrameWebView from spyder.widgets.findreplace import FindReplace from spyder.widgets.tabs import Tabs +from spyder.utils.conda import get_list_conda_envs +from spyder.utils.programs import get_interpreter_info +from spyder.utils.pyenv import get_list_pyenv_envs +from spyder.utils.workers import WorkerManager +from spyder.config.base import is_pynsist, running_in_mac_app # Localization and logging @@ -64,6 +69,7 @@ class IPythonConsoleWidgetActions: CreateCythonClient = 'create cython client' CreateSymPyClient = 'create cympy client' CreatePyLabClient = 'create pylab client' + CreateNewClientEnvironment = 'create environment client' # Current console actions ClearConsole = 'Clear shell' @@ -94,6 +100,7 @@ class IPythonConsoleWidgetActions: class IPythonConsoleWidgetOptionsMenus: SpecialConsoles = 'special_consoles_submenu' Documentation = 'documentation_submenu' + EnvironmentConsoles = 'environment_consoles_submenu' class IPythonConsoleWidgetOptionsMenuSections: @@ -268,6 +275,10 @@ def __init__(self, name=None, plugin=None, parent=None): self.interrupt_action = None self.initial_conf_options = self.get_conf_options() self.registered_spyder_kernel_handlers = {} + self.path_to_env = {} + self.envs = {} + self.value = '' + self.default_interpreter = sys.executable # Disable infowidget if requested by the user self.enable_infowidget = True @@ -351,6 +362,12 @@ def __init__(self, name=None, plugin=None, parent=None): # Initial value for the current working directory self._current_working_directory = get_home_dir() + # Worker to compute envs in a thread + self._worker_manager = WorkerManager(max_threads=1) + + # Update the list of envs at startup + self.get_envs() + def on_close(self): self.mainwindow_close = True self.close_all_clients() @@ -488,12 +505,18 @@ def setup(self): # --- Setting options menu options_menu = self.get_options_menu() + + self.console_environment_menu = self.create_menu( + IPythonConsoleWidgetOptionsMenus.EnvironmentConsoles, + _('New console in environment')) + self.special_console_menu = self.create_menu( IPythonConsoleWidgetOptionsMenus.SpecialConsoles, _('Special consoles')) for item in [ self.create_client_action, + self.console_environment_menu, self.special_console_menu, self.connect_to_kernel_action]: self.add_item_to_menu( @@ -541,6 +564,22 @@ def setup(self): icon=self.create_icon('ipython_console'), triggered=self.create_cython_client, ) + environment_consoles_names = get_list_conda_envs() #self.envs + environment_consoles = [] + for item in environment_consoles_names: + action = self.create_action( + IPythonConsoleWidgetActions.CreateNewClientEnvironment, + " ".join([item, "(", environment_consoles_names[item][1], ")"]), + icon=self.create_icon('ipython_console'), + triggered=self.create_environment_client, + ) + environment_consoles.append(action) + + for item in environment_consoles: + self.add_item_to_menu( + item, + menu=self.console_environment_menu + ) for item in [ create_pylab_action, @@ -579,6 +618,7 @@ def setup(self): for item in [ self.create_client_action, + self.console_environment_menu, self.special_console_menu, self.connect_to_kernel_action]: self.add_item_to_menu( @@ -650,6 +690,82 @@ def update_actions(self): self.syspath_action.setEnabled(not error_or_loading) self.show_time_action.setEnabled(not error_or_loading) + def _get_envs(self): + """Get the list of environments in the system.""" + # Compute info of default interpreter to have it available in + # case we need to switch to it. This will avoid lags when + # doing that in get_value. + #if self.default_interpreter not in self.path_to_env: + # self._get_env_info(self.default_interpreter) + + # Get envs + conda_env = get_list_conda_envs() + pyenv_env = get_list_pyenv_envs() + return {**conda_env, **pyenv_env} + + def _get_env_info(self, path): + """Get environment information.""" + path = path.lower() if os.name == 'nt' else path + try: + name = self.path_to_env[path] + except KeyError: + if ( + self.default_interpreter == path + and (running_in_mac_app() or is_pynsist()) + ): + name = 'internal' + elif 'conda' in path: + name = 'conda' + elif 'pyenv' in path: + name = 'pyenv' + else: + name = 'custom' + version = get_interpreter_info(path) + self.path_to_env[path] = name + self.envs[name] = (path, version) + __, version = self.envs[name] + return f'{name} ({version})' + + def get_envs(self): + """ + Get the list of environments in a thread to keep them up to + date. + """ + self._worker_manager.terminate_all() + worker = self._worker_manager.create_python_worker(self._get_envs) + worker.sig_finished.connect(self.update_envs) + worker.start() + + def update_envs(self, worker, output, error): + """Update the list of environments in the system.""" + self.envs.update(**output) + for env in list(self.envs.keys()): + path, version = self.envs[env] + # Save paths in lowercase on Windows to avoid issues with + # capitalization. + path = path.lower() if os.name == 'nt' else path + self.path_to_env[path] = env + + # self.update_context_menu() + + #def update_context_menu(self): + # """Update context menu information.""" + # environment_consoles_names = get_list_conda_envs() + # environment_consoles = [] + # for item in environment_consoles_names: + # action = self.create_action( + # IPythonConsoleWidgetActions.CreateCythonClient, + # str(item), + # icon=self.create_icon('ipython_console'), + # triggered=self.create_cython_client, + # ) + # environment_consoles.append(action) + # for item in environment_consoles: + # self.add_item_to_menu( + # item, + # menu=self.console_environment_menu + # ) + # ---- GUI options @on_conf_change(section='help', option='connect/ipython_console') def change_clients_help_connection(self, value): @@ -1354,7 +1470,7 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, """Create a new client""" self.master_clients += 1 client_id = dict(int_id=str(self.master_clients), - str_id='A') + str_id='JUANSE') client = ClientWidget( self, @@ -1481,6 +1597,10 @@ def create_cython_client(self): """Force creation of Cython client""" self.create_new_client(is_cython=True, given_name="Cython") + def create_environment_client(self): + """Force creation of Environment client""" + self.create_new_client() + @Slot(str) def create_client_from_path(self, path): """Create a client with its cwd pointing to path.""" @@ -1529,6 +1649,11 @@ def register_client(self, client): # For help requests control.sig_help_requested.connect(self.sig_help_requested) + shellwidget.sig_pdb_step.connect( + lambda fname, lineno, shellwidget=shellwidget: + self.pdb_has_stopped(fname, lineno, shellwidget)) + shellwidget.sig_pdb_state_changed.connect(self.sig_pdb_state_changed) + # To handle %edit magic petitions shellwidget.custom_edit_requested.connect(self.edit_file) From 23e7e87d7b9bf0b3745cd5076bae121da76cf45f Mon Sep 17 00:00:00 2001 From: jsbautista Date: Mon, 23 Jan 2023 12:58:18 -0500 Subject: [PATCH 02/27] Add menu consoles environment --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index a4cec3d67a9..c09389278c9 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -512,7 +512,7 @@ def setup(self): self.special_console_menu = self.create_menu( IPythonConsoleWidgetOptionsMenus.SpecialConsoles, - _('Special consoles')) + _('New special console')) for item in [ self.create_client_action, @@ -571,7 +571,7 @@ def setup(self): IPythonConsoleWidgetActions.CreateNewClientEnvironment, " ".join([item, "(", environment_consoles_names[item][1], ")"]), icon=self.create_icon('ipython_console'), - triggered=self.create_environment_client, + triggered=lambda: self.create_environment_client(item), ) environment_consoles.append(action) @@ -1466,11 +1466,11 @@ def get_current_shellwidget(self): @Slot(bool, str, bool) def create_new_client(self, give_focus=True, filename='', is_cython=False, is_pylab=False, is_sympy=False, given_name=None, - cache=True, initial_cwd=None): + cache=True, initial_cwd=None, environment=''): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=str(self.master_clients), - str_id='JUANSE') + str_id=environment) client = ClientWidget( self, @@ -1597,9 +1597,9 @@ def create_cython_client(self): """Force creation of Cython client""" self.create_new_client(is_cython=True, given_name="Cython") - def create_environment_client(self): + def create_environment_client(self, environment): """Force creation of Environment client""" - self.create_new_client() + self.create_new_client(environment=environment) @Slot(str) def create_client_from_path(self, path): From 631bf20028a7e2b7f36e46d9e67239d3be49d6fe Mon Sep 17 00:00:00 2001 From: jsbautista Date: Tue, 24 Jan 2023 13:59:01 -0500 Subject: [PATCH 03/27] Add updates to console environments --- .../ipythonconsole/utils/kernelspec.py | 7 ++++- .../ipythonconsole/widgets/main_widget.py | 30 ++++++++++--------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index 0cf60e58385..2ee5b430b68 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -99,11 +99,14 @@ class SpyderKernelSpec(KernelSpec, SpyderConfigurationAccessor): CONF_SECTION = 'ipython_console' def __init__(self, is_cython=False, is_pylab=False, - is_sympy=False, **kwargs): + is_sympy=False, is_environment=False, + path_to_environment='', **kwargs): super(SpyderKernelSpec, self).__init__(**kwargs) self.is_cython = is_cython self.is_pylab = is_pylab self.is_sympy = is_sympy + self.is_environment = is_environment + self.path_to_environment = path_to_environment self.display_name = 'Python 3 (Spyder)' self.language = 'python3' @@ -113,6 +116,8 @@ def __init__(self, is_cython=False, is_pylab=False, def argv(self): """Command to start kernels""" # Python interpreter used to start kernels + if self.is_environment: + pyexec = self.path_to_environment if self.get_conf('default', section='main_interpreter'): pyexec = get_python_executable() else: diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index c09389278c9..0c001c046f2 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -567,14 +567,17 @@ def setup(self): environment_consoles_names = get_list_conda_envs() #self.envs environment_consoles = [] for item in environment_consoles_names: + path_to_environment = environment_consoles_names[item][0] + name = str(item) action = self.create_action( - IPythonConsoleWidgetActions.CreateNewClientEnvironment, + IPythonConsoleWidgetActions.CreateNewClientEnvironment + str(item), " ".join([item, "(", environment_consoles_names[item][1], ")"]), icon=self.create_icon('ipython_console'), - triggered=lambda: self.create_environment_client(item), + triggered=lambda: self.create_environment_client( + path_to_environment=path_to_environment, environment=name), ) environment_consoles.append(action) - + for item in environment_consoles: self.add_item_to_menu( item, @@ -750,7 +753,7 @@ def update_envs(self, worker, output, error): #def update_context_menu(self): # """Update context menu information.""" - # environment_consoles_names = get_list_conda_envs() + # environment_consoles_names = get_list_conda_envs() # environment_consoles = [] # for item in environment_consoles_names: # action = self.create_action( @@ -1466,7 +1469,8 @@ def get_current_shellwidget(self): @Slot(bool, str, bool) def create_new_client(self, give_focus=True, filename='', is_cython=False, is_pylab=False, is_sympy=False, given_name=None, - cache=True, initial_cwd=None, environment=''): + cache=True, initial_cwd=None, environment='', + is_environment=False, path_to_environment=''): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=str(self.master_clients), @@ -1496,7 +1500,9 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, kernel_spec = SpyderKernelSpec( is_cython=is_cython, is_pylab=is_pylab, - is_sympy=is_sympy + is_sympy=is_sympy, + is_environment=is_environment, + path_to_environment=path_to_environment ) try: @@ -1597,9 +1603,10 @@ def create_cython_client(self): """Force creation of Cython client""" self.create_new_client(is_cython=True, given_name="Cython") - def create_environment_client(self, environment): + def create_environment_client(self, environment, path_to_environment): """Force creation of Environment client""" - self.create_new_client(environment=environment) + self.create_new_client(is_environment=True, environment=environment, + path_to_environment=path_to_environment) @Slot(str) def create_client_from_path(self, path): @@ -1648,12 +1655,7 @@ def register_client(self, client): # For help requests control.sig_help_requested.connect(self.sig_help_requested) - - shellwidget.sig_pdb_step.connect( - lambda fname, lineno, shellwidget=shellwidget: - self.pdb_has_stopped(fname, lineno, shellwidget)) - shellwidget.sig_pdb_state_changed.connect(self.sig_pdb_state_changed) - + # To handle %edit magic petitions shellwidget.custom_edit_requested.connect(self.edit_file) From e49bd1c4c06ce2c65a937a20e418944b79afd71e Mon Sep 17 00:00:00 2001 From: jsbautista Date: Tue, 24 Jan 2023 14:10:22 -0500 Subject: [PATCH 04/27] Change text in Preferences --- spyder/plugins/maininterpreter/confpage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spyder/plugins/maininterpreter/confpage.py b/spyder/plugins/maininterpreter/confpage.py index 617c9041a50..3b4fe8fb2fe 100644 --- a/spyder/plugins/maininterpreter/confpage.py +++ b/spyder/plugins/maininterpreter/confpage.py @@ -69,8 +69,8 @@ def setup_page(self): # Python executable Group pyexec_group = QGroupBox(_("Python interpreter")) pyexec_bg = QButtonGroup(pyexec_group) - pyexec_label = QLabel(_("Select the Python interpreter for all Spyder " - "consoles")) + pyexec_label = QLabel(_("Select the Python interpreter used for " + "default Spyder consoles and code completion")) self.def_exec_radio = self.create_radiobutton( _("Default (i.e. the same as Spyder's)"), 'default', From c79d6952da4f7552a6eabf64a0c94c07da832a4c Mon Sep 17 00:00:00 2001 From: jsbautista Date: Mon, 6 Feb 2023 12:46:44 -0500 Subject: [PATCH 05/27] Add updates to console environments --- .../ipythonconsole/utils/kernelspec.py | 12 ++- .../ipythonconsole/widgets/main_widget.py | 91 +++++++++---------- 2 files changed, 52 insertions(+), 51 deletions(-) diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index 2ee5b430b68..8c43b0af021 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -107,7 +107,6 @@ def __init__(self, is_cython=False, is_pylab=False, self.is_sympy = is_sympy self.is_environment = is_environment self.path_to_environment = path_to_environment - self.display_name = 'Python 3 (Spyder)' self.language = 'python3' self.resource_dir = '' @@ -117,11 +116,18 @@ def argv(self): """Command to start kernels""" # Python interpreter used to start kernels if self.is_environment: - pyexec = self.path_to_environment + self.set_conf( + 'executable', self.path_to_environment, + section='main_interpreter') + self.set_conf('default', False, section='main_interpreter') + self.set_conf('custom', True, section='main_interpreter') if self.get_conf('default', section='main_interpreter'): pyexec = get_python_executable() else: pyexec = self.get_conf('executable', section='main_interpreter') + if self.is_environment: + pyexec = self.path_to_environment + print('================================Pyexec_ :' + str(pyexec)) if not has_spyder_kernels(pyexec): raise SpyderKernelError( ERROR_SPYDER_KERNEL_INSTALLED.format( @@ -192,7 +198,7 @@ def env(self): # Environment variables that we need to pass to the kernel env_vars.update({ - 'SPY_EXTERNAL_INTERPRETER': not default_interpreter, + 'SPY_EXTERNAL_INTERPRETER': True, 'SPY_UMR_ENABLED': self.get_conf( 'umr/enabled', section='main_interpreter'), 'SPY_UMR_VERBOSE': self.get_conf( diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 0c001c046f2..bcde2bb9ecf 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -564,26 +564,8 @@ def setup(self): icon=self.create_icon('ipython_console'), triggered=self.create_cython_client, ) - environment_consoles_names = get_list_conda_envs() #self.envs - environment_consoles = [] - for item in environment_consoles_names: - path_to_environment = environment_consoles_names[item][0] - name = str(item) - action = self.create_action( - IPythonConsoleWidgetActions.CreateNewClientEnvironment + str(item), - " ".join([item, "(", environment_consoles_names[item][1], ")"]), - icon=self.create_icon('ipython_console'), - triggered=lambda: self.create_environment_client( - path_to_environment=path_to_environment, environment=name), - ) - environment_consoles.append(action) - - for item in environment_consoles: - self.add_item_to_menu( - item, - menu=self.console_environment_menu - ) - + self.console_environment_menu.aboutToShow.connect( + self.update_environment_menu) for item in [ create_pylab_action, create_sympy_action, @@ -749,26 +731,34 @@ def update_envs(self, worker, output, error): path = path.lower() if os.name == 'nt' else path self.path_to_env[path] = env - # self.update_context_menu() - - #def update_context_menu(self): - # """Update context menu information.""" - # environment_consoles_names = get_list_conda_envs() - # environment_consoles = [] - # for item in environment_consoles_names: - # action = self.create_action( - # IPythonConsoleWidgetActions.CreateCythonClient, - # str(item), - # icon=self.create_icon('ipython_console'), - # triggered=self.create_cython_client, - # ) - # environment_consoles.append(action) - # for item in environment_consoles: - # self.add_item_to_menu( - # item, - # menu=self.console_environment_menu - # ) - + def update_environment_menu(self): + """Update context menu information.""" + #self.console_environment_menu.clear() + self.get_envs() + environment_consoles_names = self.envs + environment_consoles = [] + #self.console_environment_menu.clear_actions() + for item in environment_consoles_names: + path_to_environment = str(environment_consoles_names[item][0]) + name = str(item) + action = self.create_action( + name=name, + text=item + ' ('+environment_consoles_names[item][1]+')', + icon=self.create_icon('ipython_console'), + triggered=lambda checked, environment=name, + path=path_to_environment: + self.create_environment_client( + path_to_environment=path, environment=environment), + ) + environment_consoles.append(action) + for item in environment_consoles: + self.add_item_to_menu( + item, + menu=self.console_environment_menu + ) + + + # ---- GUI options @on_conf_change(section='help', option='connect/ipython_console') def change_clients_help_connection(self, value): @@ -1388,9 +1378,10 @@ def config_options(self): cfg._merge(spy_cfg) return cfg - def interpreter_versions(self): + def interpreter_versions(self, is_environment=False, env=''): """Python and IPython versions used by clients""" - if self.get_conf('default', section='main_interpreter'): + if (self.get_conf('default', section='main_interpreter') + and not is_environment): from IPython.core import release versions = dict( python_version=sys.version, @@ -1400,6 +1391,8 @@ def interpreter_versions(self): import subprocess versions = {} pyexec = self.get_conf('executable', section='main_interpreter') + if is_environment: + pyexec = env py_cmd = u'%s -c "import sys; print(sys.version)"' % pyexec ipy_cmd = ( u'%s -c "import IPython.core.release as r; print(r.version)"' @@ -1465,12 +1458,13 @@ def get_current_shellwidget(self): @Slot(bool) @Slot(str) @Slot(bool, str) + @Slot(bool, str, str) @Slot(bool, bool) @Slot(bool, str, bool) def create_new_client(self, give_focus=True, filename='', is_cython=False, is_pylab=False, is_sympy=False, given_name=None, - cache=True, initial_cwd=None, environment='', - is_environment=False, path_to_environment=''): + cache=True, initial_cwd=None, environment='A', + is_environment=False, path_to_environment=None): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=str(self.master_clients), @@ -1483,20 +1477,21 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, additional_options=self.additional_options( is_pylab=is_pylab, is_sympy=is_sympy), - interpreter_versions=self.interpreter_versions(), + interpreter_versions=self.interpreter_versions( + is_environment, path_to_environment), context_menu_actions=self.context_menu_actions, given_name=given_name, give_focus=give_focus, handlers=self.registered_spyder_kernel_handlers, initial_cwd=initial_cwd, ) - # Add client to widget self.add_tab( client, name=client.get_name(), filename=filename, give_focus=give_focus) - # Create new kernel + + print(path_to_environment) kernel_spec = SpyderKernelSpec( is_cython=is_cython, is_pylab=is_pylab, @@ -1603,7 +1598,7 @@ def create_cython_client(self): """Force creation of Cython client""" self.create_new_client(is_cython=True, given_name="Cython") - def create_environment_client(self, environment, path_to_environment): + def create_environment_client(self, environment='', path_to_environment=''): """Force creation of Environment client""" self.create_new_client(is_environment=True, environment=environment, path_to_environment=path_to_environment) From 5e890e1b40f8172dfcbae5eb4ae7aa836651b3b4 Mon Sep 17 00:00:00 2001 From: jsbautista Date: Tue, 7 Feb 2023 13:41:27 -0500 Subject: [PATCH 06/27] Add updates to console environments --- spyder/plugins/ipythonconsole/utils/kernelspec.py | 1 - spyder/plugins/ipythonconsole/widgets/main_widget.py | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index 8c43b0af021..cf795a2bc0b 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -127,7 +127,6 @@ def argv(self): pyexec = self.get_conf('executable', section='main_interpreter') if self.is_environment: pyexec = self.path_to_environment - print('================================Pyexec_ :' + str(pyexec)) if not has_spyder_kernels(pyexec): raise SpyderKernelError( ERROR_SPYDER_KERNEL_INSTALLED.format( diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index bcde2bb9ecf..b0b1948e3ca 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -733,11 +733,10 @@ def update_envs(self, worker, output, error): def update_environment_menu(self): """Update context menu information.""" - #self.console_environment_menu.clear() self.get_envs() environment_consoles_names = self.envs environment_consoles = [] - #self.console_environment_menu.clear_actions() + self.console_environment_menu.clear_actions() for item in environment_consoles_names: path_to_environment = str(environment_consoles_names[item][0]) name = str(item) @@ -756,6 +755,7 @@ def update_environment_menu(self): item, menu=self.console_environment_menu ) + self.console_environment_menu._render() @@ -1491,7 +1491,6 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, give_focus=give_focus) # Create new kernel - print(path_to_environment) kernel_spec = SpyderKernelSpec( is_cython=is_cython, is_pylab=is_pylab, From a62410f4a09fe5d33811b1906b28df12849186ba Mon Sep 17 00:00:00 2001 From: jsbautista Date: Mon, 13 Feb 2023 13:17:19 -0500 Subject: [PATCH 07/27] Apply suggestions from code review --- .../plugins/ipythonconsole/utils/kernelspec.py | 17 ++++++++--------- .../ipythonconsole/widgets/main_widget.py | 8 ++++---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index cf795a2bc0b..e5e68441ec6 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -99,14 +99,13 @@ class SpyderKernelSpec(KernelSpec, SpyderConfigurationAccessor): CONF_SECTION = 'ipython_console' def __init__(self, is_cython=False, is_pylab=False, - is_sympy=False, is_environment=False, - path_to_environment='', **kwargs): + is_sympy=False, path_to_custom_interpreter=None, + **kwargs): super(SpyderKernelSpec, self).__init__(**kwargs) self.is_cython = is_cython self.is_pylab = is_pylab self.is_sympy = is_sympy - self.is_environment = is_environment - self.path_to_environment = path_to_environment + self.path_to_custom_interpreter = path_to_custom_interpreter self.display_name = 'Python 3 (Spyder)' self.language = 'python3' self.resource_dir = '' @@ -115,9 +114,9 @@ def __init__(self, is_cython=False, is_pylab=False, def argv(self): """Command to start kernels""" # Python interpreter used to start kernels - if self.is_environment: + if self.path_to_custom_interpreter: self.set_conf( - 'executable', self.path_to_environment, + 'executable', self.path_to_custom_interpreter, section='main_interpreter') self.set_conf('default', False, section='main_interpreter') self.set_conf('custom', True, section='main_interpreter') @@ -125,8 +124,8 @@ def argv(self): pyexec = get_python_executable() else: pyexec = self.get_conf('executable', section='main_interpreter') - if self.is_environment: - pyexec = self.path_to_environment + if self.path_to_custom_interpreter: + pyexec = self.path_to_custom_interpreter if not has_spyder_kernels(pyexec): raise SpyderKernelError( ERROR_SPYDER_KERNEL_INSTALLED.format( @@ -197,7 +196,7 @@ def env(self): # Environment variables that we need to pass to the kernel env_vars.update({ - 'SPY_EXTERNAL_INTERPRETER': True, + 'SPY_EXTERNAL_INTERPRETER': self.path_to_custom_interpreter, 'SPY_UMR_ENABLED': self.get_conf( 'umr/enabled', section='main_interpreter'), 'SPY_UMR_VERBOSE': self.get_conf( diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index b0b1948e3ca..82db8a09831 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1378,10 +1378,10 @@ def config_options(self): cfg._merge(spy_cfg) return cfg - def interpreter_versions(self, is_environment=False, env=''): + def interpreter_versions(self, path_to_custom_interpreter=None): """Python and IPython versions used by clients""" if (self.get_conf('default', section='main_interpreter') - and not is_environment): + and not path_to_custom_interpreter): from IPython.core import release versions = dict( python_version=sys.version, @@ -1391,8 +1391,8 @@ def interpreter_versions(self, is_environment=False, env=''): import subprocess versions = {} pyexec = self.get_conf('executable', section='main_interpreter') - if is_environment: - pyexec = env + if path_to_custom_interpreter: + pyexec = path_to_custom_interpreter py_cmd = u'%s -c "import sys; print(sys.version)"' % pyexec ipy_cmd = ( u'%s -c "import IPython.core.release as r; print(r.version)"' From d33be3122a809ca15e4e8945f720016abfc941bc Mon Sep 17 00:00:00 2001 From: jsbautista <42411448+jsbautista@users.noreply.github.com> Date: Mon, 13 Feb 2023 12:19:14 -0500 Subject: [PATCH 08/27] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 82db8a09831..cbb94a4923d 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -732,7 +732,7 @@ def update_envs(self, worker, output, error): self.path_to_env[path] = env def update_environment_menu(self): - """Update context menu information.""" + """Update context menu entries to select specific interpreter to launch a console.""" self.get_envs() environment_consoles_names = self.envs environment_consoles = [] @@ -757,8 +757,6 @@ def update_environment_menu(self): ) self.console_environment_menu._render() - - # ---- GUI options @on_conf_change(section='help', option='connect/ipython_console') def change_clients_help_connection(self, value): @@ -1490,7 +1488,6 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, client, name=client.get_name(), filename=filename, give_focus=give_focus) # Create new kernel - kernel_spec = SpyderKernelSpec( is_cython=is_cython, is_pylab=is_pylab, @@ -1649,7 +1646,6 @@ def register_client(self, client): # For help requests control.sig_help_requested.connect(self.sig_help_requested) - # To handle %edit magic petitions shellwidget.custom_edit_requested.connect(self.edit_file) From ab1d363973a464c7c81ad0c68bfc2c838a08ab3c Mon Sep 17 00:00:00 2001 From: jsbautista Date: Thu, 16 Feb 2023 20:06:18 -0500 Subject: [PATCH 09/27] Apply suggestions from code review --- .../plugins/ipythonconsole/utils/kernelspec.py | 9 ++------- .../ipythonconsole/widgets/main_widget.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index e5e68441ec6..95f903218cd 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -114,13 +114,8 @@ def __init__(self, is_cython=False, is_pylab=False, def argv(self): """Command to start kernels""" # Python interpreter used to start kernels - if self.path_to_custom_interpreter: - self.set_conf( - 'executable', self.path_to_custom_interpreter, - section='main_interpreter') - self.set_conf('default', False, section='main_interpreter') - self.set_conf('custom', True, section='main_interpreter') - if self.get_conf('default', section='main_interpreter'): + if (self.get_conf('default', section='main_interpreter') and + not self.path_to_custom_interpreter): pyexec = get_python_executable() else: pyexec = self.get_conf('executable', section='main_interpreter') diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index cbb94a4923d..848d8ff357f 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -747,7 +747,7 @@ def update_environment_menu(self): triggered=lambda checked, environment=name, path=path_to_environment: self.create_environment_client( - path_to_environment=path, environment=environment), + path_to_custom_interpreter=path, environment=environment), ) environment_consoles.append(action) for item in environment_consoles: @@ -1462,7 +1462,7 @@ def get_current_shellwidget(self): def create_new_client(self, give_focus=True, filename='', is_cython=False, is_pylab=False, is_sympy=False, given_name=None, cache=True, initial_cwd=None, environment='A', - is_environment=False, path_to_environment=None): + path_to_custom_interpreter=None): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=str(self.master_clients), @@ -1476,7 +1476,7 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, is_pylab=is_pylab, is_sympy=is_sympy), interpreter_versions=self.interpreter_versions( - is_environment, path_to_environment), + path_to_custom_interpreter), context_menu_actions=self.context_menu_actions, given_name=given_name, give_focus=give_focus, @@ -1492,8 +1492,7 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, is_cython=is_cython, is_pylab=is_pylab, is_sympy=is_sympy, - is_environment=is_environment, - path_to_environment=path_to_environment + path_to_custom_interpreter=path_to_custom_interpreter ) try: @@ -1594,10 +1593,11 @@ def create_cython_client(self): """Force creation of Cython client""" self.create_new_client(is_cython=True, given_name="Cython") - def create_environment_client(self, environment='', path_to_environment=''): + def create_environment_client(self, environment='', + path_to_custom_interpreter=''): """Force creation of Environment client""" - self.create_new_client(is_environment=True, environment=environment, - path_to_environment=path_to_environment) + self.create_new_client(environment=environment, + path_to_custom_interpreter=path_to_custom_interpreter) @Slot(str) def create_client_from_path(self, path): From d946fee49155309e5a7b563e12ca3a3021f7e5d2 Mon Sep 17 00:00:00 2001 From: jsbautista Date: Mon, 27 Feb 2023 14:00:52 -0500 Subject: [PATCH 10/27] Apply suggestions from code review --- .../ipythonconsole/widgets/main_widget.py | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 848d8ff357f..a98204a77d2 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -45,11 +45,9 @@ from spyder.widgets.browser import FrameWebView from spyder.widgets.findreplace import FindReplace from spyder.widgets.tabs import Tabs +from spyder.utils.workers import WorkerManager from spyder.utils.conda import get_list_conda_envs -from spyder.utils.programs import get_interpreter_info from spyder.utils.pyenv import get_list_pyenv_envs -from spyder.utils.workers import WorkerManager -from spyder.config.base import is_pynsist, running_in_mac_app # Localization and logging @@ -274,8 +272,7 @@ def __init__(self, name=None, plugin=None, parent=None): self.run_cell_filename = None self.interrupt_action = None self.initial_conf_options = self.get_conf_options() - self.registered_spyder_kernel_handlers = {} - self.path_to_env = {} + self.registered_spyder_kernel_handlers = {} self.envs = {} self.value = '' self.default_interpreter = sys.executable @@ -680,37 +677,12 @@ def _get_envs(self): # Compute info of default interpreter to have it available in # case we need to switch to it. This will avoid lags when # doing that in get_value. - #if self.default_interpreter not in self.path_to_env: - # self._get_env_info(self.default_interpreter) # Get envs conda_env = get_list_conda_envs() pyenv_env = get_list_pyenv_envs() return {**conda_env, **pyenv_env} - def _get_env_info(self, path): - """Get environment information.""" - path = path.lower() if os.name == 'nt' else path - try: - name = self.path_to_env[path] - except KeyError: - if ( - self.default_interpreter == path - and (running_in_mac_app() or is_pynsist()) - ): - name = 'internal' - elif 'conda' in path: - name = 'conda' - elif 'pyenv' in path: - name = 'pyenv' - else: - name = 'custom' - version = get_interpreter_info(path) - self.path_to_env[path] = name - self.envs[name] = (path, version) - __, version = self.envs[name] - return f'{name} ({version})' - def get_envs(self): """ Get the list of environments in a thread to keep them up to @@ -724,12 +696,6 @@ def get_envs(self): def update_envs(self, worker, output, error): """Update the list of environments in the system.""" self.envs.update(**output) - for env in list(self.envs.keys()): - path, version = self.envs[env] - # Save paths in lowercase on Windows to avoid issues with - # capitalization. - path = path.lower() if os.name == 'nt' else path - self.path_to_env[path] = env def update_environment_menu(self): """Update context menu entries to select specific interpreter to launch a console.""" From cde591f1d1d2e64148e0bf10202c8223775ebf24 Mon Sep 17 00:00:00 2001 From: jsbautista <42411448+jsbautista@users.noreply.github.com> Date: Tue, 28 Feb 2023 21:26:13 -0500 Subject: [PATCH 11/27] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- spyder/plugins/ipythonconsole/utils/kernelspec.py | 2 +- spyder/plugins/ipythonconsole/widgets/main_widget.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index 95f903218cd..72735b16ba5 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -191,7 +191,7 @@ def env(self): # Environment variables that we need to pass to the kernel env_vars.update({ - 'SPY_EXTERNAL_INTERPRETER': self.path_to_custom_interpreter, + 'SPY_EXTERNAL_INTERPRETER': not default_interpreter or self.path_to_custom_interpreter, 'SPY_UMR_ENABLED': self.get_conf( 'umr/enabled', section='main_interpreter'), 'SPY_UMR_VERBOSE': self.get_conf( diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index a98204a77d2..a5711736866 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -708,7 +708,7 @@ def update_environment_menu(self): name = str(item) action = self.create_action( name=name, - text=item + ' ('+environment_consoles_names[item][1]+')', + text=f'{item} ({environment_consoles_names[item][1]})', icon=self.create_icon('ipython_console'), triggered=lambda checked, environment=name, path=path_to_environment: From a347fc5442b3d4037ad25fa50d2a12282c63c42f Mon Sep 17 00:00:00 2001 From: jsbautista Date: Sun, 5 Mar 2023 19:15:15 -0500 Subject: [PATCH 12/27] Apply suggestions from code review --- spyder/plugins/ipythonconsole/widgets/client.py | 9 ++++++--- spyder/plugins/ipythonconsole/widgets/main_widget.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/spyder/plugins/ipythonconsole/widgets/client.py b/spyder/plugins/ipythonconsole/widgets/client.py index 19bddb6d624..7360434cfb9 100644 --- a/spyder/plugins/ipythonconsole/widgets/client.py +++ b/spyder/plugins/ipythonconsole/widgets/client.py @@ -520,13 +520,16 @@ def get_name(self): else: name = self.hostname # Adding id to name - client_id = self.id_['int_id'] + u'/' + self.id_['str_id'] + client_id = (self.id_['int_id'] + u'/' + self.id_['str_id'] + + '(' + self.id_['str_env_name'] + ')') name = name + u' ' + client_id elif self.given_name in ["Pylab", "SymPy", "Cython"]: - client_id = self.id_['int_id'] + u'/' + self.id_['str_id'] + client_id = (self.id_['int_id'] + u'/' + self.id_['str_id'] + + '(' + self.id_['str_env_name'] + ')') name = self.given_name + u' ' + client_id else: - name = self.given_name + u'/' + self.id_['str_id'] + name = (self.given_name + u'/' + self.id_['str_id'] + + '(' + self.id_['str_env_name'] + ')') return name def get_control(self): diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index a98204a77d2..4af954f2325 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1427,12 +1427,12 @@ def get_current_shellwidget(self): @Slot(bool, str, bool) def create_new_client(self, give_focus=True, filename='', is_cython=False, is_pylab=False, is_sympy=False, given_name=None, - cache=True, initial_cwd=None, environment='A', + cache=True, initial_cwd=None, environment='Default', path_to_custom_interpreter=None): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=str(self.master_clients), - str_id=environment) + str_id='A', str_env_name=environment) client = ClientWidget( self, From 8f940b1bc4f10d1b0a4cdc385b09012f4cef372c Mon Sep 17 00:00:00 2001 From: jsbautista Date: Sun, 5 Mar 2023 23:06:08 -0500 Subject: [PATCH 13/27] Apply suggestions from code review --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 1a6009f9eb2..4983af65c09 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -714,6 +714,7 @@ def update_environment_menu(self): path=path_to_environment: self.create_environment_client( path_to_custom_interpreter=path, environment=environment), + overwrite=True ) environment_consoles.append(action) for item in environment_consoles: From dc30583d1f1e665e8fb1019f5b6786ad997d5633 Mon Sep 17 00:00:00 2001 From: jsbautista Date: Tue, 7 Mar 2023 22:47:39 -0500 Subject: [PATCH 14/27] Apply suggestions --- spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py | 2 +- spyder/plugins/ipythonconsole/widgets/client.py | 6 +++--- spyder/plugins/ipythonconsole/widgets/main_widget.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index dc14eb9b3af..58e8627a465 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -2022,7 +2022,7 @@ def test_run_script(ipyconsole, qtbot, tmp_path): # Validate created consoles names and code executed for filename in filenames: basename = osp.basename(filename) - client_name = f'{basename}/A' + client_name = f'{basename}/A (default)' variable_name = basename.split('.')[0] client = ipyconsole.get_client_for_file(filename) diff --git a/spyder/plugins/ipythonconsole/widgets/client.py b/spyder/plugins/ipythonconsole/widgets/client.py index 7360434cfb9..fe273b622fb 100644 --- a/spyder/plugins/ipythonconsole/widgets/client.py +++ b/spyder/plugins/ipythonconsole/widgets/client.py @@ -521,15 +521,15 @@ def get_name(self): name = self.hostname # Adding id to name client_id = (self.id_['int_id'] + u'/' + self.id_['str_id'] - + '(' + self.id_['str_env_name'] + ')') + + ' (' + self.id_['str_env_name'] + ')') name = name + u' ' + client_id elif self.given_name in ["Pylab", "SymPy", "Cython"]: client_id = (self.id_['int_id'] + u'/' + self.id_['str_id'] - + '(' + self.id_['str_env_name'] + ')') + + ' (' + self.id_['str_env_name'] + ')') name = self.given_name + u' ' + client_id else: name = (self.given_name + u'/' + self.id_['str_id'] + - '(' + self.id_['str_env_name'] + ')') + ' (' + self.id_['str_env_name'] + ')') return name def get_control(self): diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 4983af65c09..525c9e064c1 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -710,7 +710,7 @@ def update_environment_menu(self): name=name, text=f'{item} ({environment_consoles_names[item][1]})', icon=self.create_icon('ipython_console'), - triggered=lambda checked, environment=name, + triggered=lambda checked, environment=name.split()[1], path=path_to_environment: self.create_environment_client( path_to_custom_interpreter=path, environment=environment), From ef84f78ed10a506e3d915c573bfcbf07c968d525 Mon Sep 17 00:00:00 2001 From: jsbautista Date: Tue, 7 Mar 2023 23:28:50 -0500 Subject: [PATCH 15/27] Apply suggestions --- spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 7b74957d71f..b1bedcab1dd 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -2018,7 +2018,7 @@ def test_run_script(ipyconsole, qtbot, tmp_path): # Validate created consoles names and code executed for filename in filenames: basename = osp.basename(filename) - client_name = f'{basename}/A (default)' + client_name = f'{basename}/A (Default)' variable_name = basename.split('.')[0] client = ipyconsole.get_client_for_file(filename) From 0aa166272dbbe66824ab4abadb3e26f74017cf49 Mon Sep 17 00:00:00 2001 From: jsbautista Date: Sun, 12 Mar 2023 15:40:44 -0500 Subject: [PATCH 16/27] Apply suggestions --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 956a9ec407d..aa7222b12af 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1500,14 +1500,16 @@ def create_client_for_kernel(self, connection_file, hostname, sshkey, # Set full client name client_id = dict(int_id=master_client.id_['int_id'], - str_id=chr(slave_ord + 1)) + str_id=chr(slave_ord + 1), + str_env_name=master_client.id_['str_env_name']) else: # If we couldn't find a client with the same connection file, # it means this is a new master client self.master_clients += 1 # Set full client name - client_id = dict(int_id=str(self.master_clients), str_id='A') + client_id = dict(int_id=str(self.master_clients), str_id='A', + str_env_name=_("Unknown")) # Creating the client client = ClientWidget( From 867b8fe1e59169125ec70dd37182953c311b5c3d Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 14 Mar 2023 17:54:02 -0500 Subject: [PATCH 17/27] Update IPython console plugin tests --- .../ipythonconsole/tests/test_ipythonconsole.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index b1bedcab1dd..d9fe86a7921 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -350,15 +350,15 @@ def test_console_disambiguation(ipyconsole, qtbot): # Create new client and assert name without disambiguation ipyconsole.create_client_for_file(filename_b) client = ipyconsole.get_current_client() - assert client.get_name() == 'c.py/A' + assert client.get_name() == 'c.py/A (Default)' # Create new client and assert name with disambiguation ipyconsole.create_client_for_file(filename_d) client = ipyconsole.get_current_client() - assert client.get_name() == 'c.py - d/A' + assert client.get_name() == 'c.py - d/A (Default)' ipyconsole.get_widget().tabwidget.setCurrentIndex(1) client = ipyconsole.get_current_client() - assert client.get_name() == 'c.py - b/A' + assert client.get_name() == 'c.py - b/A (Default)' @flaky(max_runs=3) @@ -874,7 +874,9 @@ def test_load_kernel_file_from_id(ipyconsole, qtbot): qtbot.waitUntil(lambda: len(ipyconsole.get_clients()) == 2) new_client = ipyconsole.get_clients()[1] - assert new_client.id_ == dict(int_id='1', str_id='B') + assert new_client.id_ == dict( + int_id='1', str_id='B', str_env_name='Default' + ) @flaky(max_runs=3) @@ -915,7 +917,9 @@ def test_load_kernel_file(ipyconsole, qtbot, tmpdir): with qtbot.waitSignal(new_shell.executed): new_shell.execute('a = 10') - assert new_client.id_ == dict(int_id='1', str_id='B') + assert new_client.id_ == dict( + int_id='1', str_id='B', str_env_name='Default' + ) assert shell.get_value('a') == new_shell.get_value('a') From 4ba249a77e31d260161da3fce5226d3b6930808d Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 14 Mar 2023 18:25:55 -0500 Subject: [PATCH 18/27] Fix IPython console mainwindow test --- spyder/app/tests/test_mainwindow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index a1cfc9b346b..2a05affbd5c 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -816,7 +816,7 @@ def test_dedicated_consoles(main_window, qtbot): assert len(main_window.ipyconsole.get_clients()) == 2 assert main_window.ipyconsole.get_widget().filenames == ['', test_file] - assert main_window.ipyconsole.get_widget().tabwidget.tabText(1) == 'script.py/A' + assert main_window.ipyconsole.get_widget().tabwidget.tabText(1) == 'script.py/A (Default)' qtbot.waitUntil(lambda: nsb.editor.source_model.rowCount() == 4) assert nsb.editor.source_model.rowCount() == 4 From 4bb02d16c3869780f43f0e91f6261fb8c4c88393 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 15 Mar 2023 13:25:54 -0500 Subject: [PATCH 19/27] Fix some code style issues. Refactor getting envs information --- .../ipythonconsole/widgets/main_widget.py | 35 +++++++------------ .../plugins/maininterpreter/widgets/status.py | 29 +++++++-------- spyder/utils/envs.py | 24 +++++++++++++ 3 files changed, 49 insertions(+), 39 deletions(-) create mode 100644 spyder/utils/envs.py diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index aa7222b12af..c2fb6c65d7a 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -46,8 +46,7 @@ from spyder.widgets.findreplace import FindReplace from spyder.widgets.tabs import Tabs from spyder.utils.workers import WorkerManager -from spyder.utils.conda import get_list_conda_envs -from spyder.utils.pyenv import get_list_pyenv_envs +from spyder.utils.envs import get_list_envs # Logging @@ -271,7 +270,7 @@ def __init__(self, name=None, plugin=None, parent=None): self.run_cell_filename = None self.interrupt_action = None self.initial_conf_options = self.get_conf_options() - self.registered_spyder_kernel_handlers = {} + self.registered_spyder_kernel_handlers = {} self.envs = {} self.value = '' self.default_interpreter = sys.executable @@ -671,24 +670,12 @@ def update_actions(self): self.syspath_action.setEnabled(not error_or_loading) self.show_time_action.setEnabled(not error_or_loading) - def _get_envs(self): - """Get the list of environments in the system.""" - # Compute info of default interpreter to have it available in - # case we need to switch to it. This will avoid lags when - # doing that in get_value. - - # Get envs - conda_env = get_list_conda_envs() - pyenv_env = get_list_pyenv_envs() - return {**conda_env, **pyenv_env} - def get_envs(self): """ - Get the list of environments in a thread to keep them up to - date. + Get the list of environments/interpreters in a worker. """ self._worker_manager.terminate_all() - worker = self._worker_manager.create_python_worker(self._get_envs) + worker = self._worker_manager.create_python_worker(get_list_envs) worker.sig_finished.connect(self.update_envs) worker.start() @@ -697,7 +684,9 @@ def update_envs(self, worker, output, error): self.envs.update(**output) def update_environment_menu(self): - """Update context menu entries to select specific interpreter to launch a console.""" + """ + Update context menu submenu with entries for available interpreters. + """ self.get_envs() environment_consoles_names = self.envs environment_consoles = [] @@ -722,7 +711,7 @@ def update_environment_menu(self): menu=self.console_environment_menu ) self.console_environment_menu._render() - + # ---- GUI options @on_conf_change(section='help', option='connect/ipython_console') def change_clients_help_connection(self, value): @@ -1563,9 +1552,11 @@ def create_cython_client(self): def create_environment_client(self, environment='', path_to_custom_interpreter=''): - """Force creation of Environment client""" - self.create_new_client(environment=environment, - path_to_custom_interpreter=path_to_custom_interpreter) + """Force creation of Environment client.""" + self.create_new_client( + environment=environment, + path_to_custom_interpreter=path_to_custom_interpreter + ) @Slot(str) def create_client_from_path(self, path): diff --git a/spyder/plugins/maininterpreter/widgets/status.py b/spyder/plugins/maininterpreter/widgets/status.py index 3cb3f8cf7c9..0c5750f08dd 100644 --- a/spyder/plugins/maininterpreter/widgets/status.py +++ b/spyder/plugins/maininterpreter/widgets/status.py @@ -16,12 +16,10 @@ from qtpy.QtCore import QTimer, Signal # Local imports -from spyder.api.translations import _ from spyder.api.widgets.status import BaseTimerStatus from spyder.config.base import is_pynsist, running_in_mac_app -from spyder.utils.conda import get_list_conda_envs +from spyder.utils.envs import get_list_envs from spyder.utils.programs import get_interpreter_info -from spyder.utils.pyenv import get_list_pyenv_envs from spyder.utils.workers import WorkerManager @@ -107,19 +105,6 @@ def _get_env_dir(self, interpreter): else: return osp.dirname(osp.dirname(interpreter)) - def _get_envs(self): - """Get the list of environments in the system.""" - # Compute info of default interpreter to have it available in - # case we need to switch to it. This will avoid lags when - # doing that in get_value. - if self.default_interpreter not in self.path_to_env: - self._get_env_info(self.default_interpreter) - - # Get envs - conda_env = get_list_conda_envs() - pyenv_env = get_list_pyenv_envs() - return {**conda_env, **pyenv_env} - def _get_env_info(self, path): """Get environment information.""" path = path.lower() if os.name == 'nt' else path @@ -164,7 +149,17 @@ def get_envs(self): date. """ self._worker_manager.terminate_all() - worker = self._worker_manager.create_python_worker(self._get_envs) + + # Compute info of default interpreter to have it available in + # case we need to switch to it. This will avoid lags when + # doing that in get_value. + if self.default_interpreter not in self.path_to_env: + default_worker = self._worker_manager.create_python_worker( + self._get_env_info, + self.default_interpreter + ) + default_worker.start() + worker = self._worker_manager.create_python_worker(get_list_envs) worker.sig_finished.connect(self.update_envs) worker.start() diff --git a/spyder/utils/envs.py b/spyder/utils/envs.py new file mode 100644 index 00000000000..e70bc6c2c18 --- /dev/null +++ b/spyder/utils/envs.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# +# Copyright © Spyder Project Contributors +# Licensed under the terms of the MIT License +# (see spyder/__init__.py for details) + +""" +Python environment general utilities +""" + +from spyder.utils.conda import get_list_conda_envs +from spyder.utils.pyenv import get_list_pyenv_envs + + +def get_list_envs(): + """ + Get the list of environments in the system. + + Currently detected conda and pyenv based environments. + """ + conda_env = get_list_conda_envs() + pyenv_env = get_list_pyenv_envs() + + return {**conda_env, **pyenv_env} From 2c04843d73a413c632316905f2670e8eb1c64b52 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 15 Mar 2023 13:52:37 -0500 Subject: [PATCH 20/27] Restore some removed blank lines. Update envs module docstring --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 3 +++ spyder/utils/envs.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index c2fb6c65d7a..2d079ff9fc3 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -1438,10 +1438,12 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, handlers=self.registered_spyder_kernel_handlers, initial_cwd=initial_cwd, ) + # Add client to widget self.add_tab( client, name=client.get_name(), filename=filename, give_focus=give_focus) + # Create new kernel kernel_spec = SpyderKernelSpec( is_cython=is_cython, @@ -1605,6 +1607,7 @@ def register_client(self, client): # For help requests control.sig_help_requested.connect(self.sig_help_requested) + # To handle %edit magic petitions shellwidget.custom_edit_requested.connect(self.edit_file) diff --git a/spyder/utils/envs.py b/spyder/utils/envs.py index e70bc6c2c18..2cb72773865 100644 --- a/spyder/utils/envs.py +++ b/spyder/utils/envs.py @@ -5,7 +5,7 @@ # (see spyder/__init__.py for details) """ -Python environment general utilities +Python environments general utilities """ from spyder.utils.conda import get_list_conda_envs From 373ad2512f03fae66019a27d789aca6ec4bcfa9e Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 14 Apr 2023 14:58:55 -0500 Subject: [PATCH 21/27] Simplify naming approach for the consoles tabs when created from the new submenu --- spyder/app/tests/test_mainwindow.py | 2 +- .../tests/test_ipythonconsole.py | 12 ++--- .../plugins/ipythonconsole/widgets/client.py | 16 +++---- .../ipythonconsole/widgets/main_widget.py | 45 +++++++++---------- 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index d3f1264db3c..5cb3faba5f9 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -817,7 +817,7 @@ def test_dedicated_consoles(main_window, qtbot): assert len(main_window.ipyconsole.get_clients()) == 2 assert main_window.ipyconsole.get_widget().filenames == ['', test_file] - assert main_window.ipyconsole.get_widget().tabwidget.tabText(1) == 'script.py/A (Default)' + assert main_window.ipyconsole.get_widget().tabwidget.tabText(1) == 'script.py/A' qtbot.waitUntil(lambda: nsb.editor.source_model.rowCount() == 4) assert nsb.editor.source_model.rowCount() == 4 diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 44298ef3657..40bed6023a7 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -350,15 +350,15 @@ def test_console_disambiguation(ipyconsole, qtbot): # Create new client and assert name without disambiguation ipyconsole.create_client_for_file(filename_b) client = ipyconsole.get_current_client() - assert client.get_name() == 'c.py/A (Default)' + assert client.get_name() == 'c.py/A' # Create new client and assert name with disambiguation ipyconsole.create_client_for_file(filename_d) client = ipyconsole.get_current_client() - assert client.get_name() == 'c.py - d/A (Default)' + assert client.get_name() == 'c.py - d/A' ipyconsole.get_widget().tabwidget.setCurrentIndex(1) client = ipyconsole.get_current_client() - assert client.get_name() == 'c.py - b/A (Default)' + assert client.get_name() == 'c.py - b/A' @flaky(max_runs=3) @@ -875,7 +875,7 @@ def test_load_kernel_file_from_id(ipyconsole, qtbot): new_client = ipyconsole.get_clients()[1] assert new_client.id_ == dict( - int_id='1', str_id='B', str_env_name='Default' + int_id='1', str_id='B', str_env_name='' ) @@ -918,7 +918,7 @@ def test_load_kernel_file(ipyconsole, qtbot, tmpdir): new_shell.execute('a = 10') assert new_client.id_ == dict( - int_id='1', str_id='B', str_env_name='Default' + int_id='1', str_id='B', str_env_name='' ) assert shell.get_value('a') == new_shell.get_value('a') @@ -2041,7 +2041,7 @@ def test_run_script(ipyconsole, qtbot, tmp_path): # Validate created consoles names and code executed for filename in filenames: basename = osp.basename(filename) - client_name = f'{basename}/A (Default)' + client_name = f'{basename}/A' variable_name = basename.split('.')[0] client = ipyconsole.get_client_for_file(filename) diff --git a/spyder/plugins/ipythonconsole/widgets/client.py b/spyder/plugins/ipythonconsole/widgets/client.py index 9bc78d47835..9d032202679 100644 --- a/spyder/plugins/ipythonconsole/widgets/client.py +++ b/spyder/plugins/ipythonconsole/widgets/client.py @@ -98,7 +98,8 @@ def __init__(self, parent, id_, give_focus=True, options_button=None, handlers={}, - initial_cwd=None): + initial_cwd=None, + forcing_custom_interpreter=False): super(ClientWidget, self).__init__(parent) SaveHistoryMixin.__init__(self, get_conf_path('history.py')) @@ -108,6 +109,7 @@ def __init__(self, parent, id_, self.menu_actions = menu_actions self.given_name = given_name self.initial_cwd = initial_cwd + self.forcing_custom_interpreter = forcing_custom_interpreter # --- Other attrs self.kernel_handler = None @@ -516,16 +518,14 @@ def get_name(self): else: name = self.hostname # Adding id to name - client_id = (self.id_['int_id'] + u'/' + self.id_['str_id'] - + ' (' + self.id_['str_env_name'] + ')') + client_id = (self.id_['int_id'] + u'/' + self.id_['str_id']) name = name + u' ' + client_id - elif self.given_name in ["Pylab", "SymPy", "Cython"]: - client_id = (self.id_['int_id'] + u'/' + self.id_['str_id'] - + ' (' + self.id_['str_env_name'] + ')') + elif (self.given_name in ["Pylab", "SymPy", "Cython"] or + self.forcing_custom_interpreter): + client_id = (self.id_['int_id'] + u'/' + self.id_['str_id']) name = self.given_name + u' ' + client_id else: - name = (self.given_name + u'/' + self.id_['str_id'] + - ' (' + self.id_['str_env_name'] + ')') + name = (self.given_name + u'/' + self.id_['str_id']) return name def get_control(self): diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 72b916e3178..0bc87a3b5ca 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -273,7 +273,6 @@ def __init__(self, name=None, plugin=None, parent=None): self.initial_conf_options = self.get_conf_options() self.registered_spyder_kernel_handlers = {} self.envs = {} - self.value = '' self.default_interpreter = sys.executable # Disable infowidget if requested by the user @@ -689,26 +688,26 @@ def update_environment_menu(self): Update context menu submenu with entries for available interpreters. """ self.get_envs() - environment_consoles_names = self.envs - environment_consoles = [] self.console_environment_menu.clear_actions() - for item in environment_consoles_names: - path_to_environment = str(environment_consoles_names[item][0]) - name = str(item) + for env_key, env_info in self.envs.items(): + env_name = env_key.split()[-1] + path_to_interpreter, python_version = env_info action = self.create_action( - name=name, - text=f'{item} ({environment_consoles_names[item][1]})', + name=env_key, + text=f'{env_key} ({python_version})', icon=self.create_icon('ipython_console'), - triggered=lambda checked, environment=name.split()[1], - path=path_to_environment: + triggered=( + lambda checked, env_name=env_name, + path_to_interpreter=path_to_interpreter: self.create_environment_client( - path_to_custom_interpreter=path, environment=environment), + env_name, + path_to_interpreter + ) + ), overwrite=True ) - environment_consoles.append(action) - for item in environment_consoles: self.add_item_to_menu( - item, + action, menu=self.console_environment_menu ) self.console_environment_menu._render() @@ -1417,12 +1416,12 @@ def get_current_shellwidget(self): @Slot(bool, str, bool) def create_new_client(self, give_focus=True, filename='', is_cython=False, is_pylab=False, is_sympy=False, given_name=None, - cache=True, initial_cwd=None, environment='Default', + cache=True, initial_cwd=None, path_to_custom_interpreter=None): """Create a new client""" self.master_clients += 1 client_id = dict(int_id=str(self.master_clients), - str_id='A', str_env_name=environment) + str_id='A') client = ClientWidget( self, @@ -1438,6 +1437,7 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, give_focus=give_focus, handlers=self.registered_spyder_kernel_handlers, initial_cwd=initial_cwd, + forcing_custom_interpreter=path_to_custom_interpreter is not None ) # Add client to widget @@ -1492,16 +1492,14 @@ def create_client_for_kernel(self, connection_file, hostname, sshkey, # Set full client name client_id = dict(int_id=master_client.id_['int_id'], - str_id=chr(slave_ord + 1), - str_env_name=master_client.id_['str_env_name']) + str_id=chr(slave_ord + 1)) else: # If we couldn't find a client with the same connection file, # it means this is a new master client self.master_clients += 1 # Set full client name - client_id = dict(int_id=str(self.master_clients), str_id='A', - str_env_name=_("Unknown")) + client_id = dict(int_id=str(self.master_clients), str_id='A') # Creating the client client = ClientWidget( @@ -1553,11 +1551,12 @@ def create_cython_client(self): """Force creation of Cython client""" self.create_new_client(is_cython=True, given_name="Cython") - def create_environment_client(self, environment='', - path_to_custom_interpreter=''): + def create_environment_client( + self, environment, path_to_custom_interpreter + ): """Force creation of Environment client.""" self.create_new_client( - environment=environment, + given_name=environment, path_to_custom_interpreter=path_to_custom_interpreter ) From f3164cb3b7badea2eda0554ce02c064fe289f900 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 17 Apr 2023 13:13:45 -0500 Subject: [PATCH 22/27] Fix IPython console test --- spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 40bed6023a7..5c4e3264ca8 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -874,9 +874,7 @@ def test_load_kernel_file_from_id(ipyconsole, qtbot): qtbot.waitUntil(lambda: len(ipyconsole.get_clients()) == 2) new_client = ipyconsole.get_clients()[1] - assert new_client.id_ == dict( - int_id='1', str_id='B', str_env_name='' - ) + assert new_client.id_ == dict(int_id='1', str_id='B') @flaky(max_runs=3) From 778d6cf31885677c17a94b05d4f6559728e0c4f4 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 17 Apr 2023 13:29:47 -0500 Subject: [PATCH 23/27] Add style from QMenu to not expand more than one column --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 0bc87a3b5ca..c6a7b02e041 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -504,6 +504,9 @@ def setup(self): self.console_environment_menu = self.create_menu( IPythonConsoleWidgetOptionsMenus.EnvironmentConsoles, _('New console in environment')) + self.console_environment_menu.setStyleSheet( + "QMenu { menu-scrollable: 1; }" + ) self.special_console_menu = self.create_menu( IPythonConsoleWidgetOptionsMenus.SpecialConsoles, From fc3afdfb2cc0ec9d4d825b12ab2d2114752595fc Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 17 Apr 2023 14:11:08 -0500 Subject: [PATCH 24/27] Fix IPython console test --- spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 7c18ed90e06..2f13af8c46d 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -916,9 +916,7 @@ def test_load_kernel_file(ipyconsole, qtbot, tmpdir): with qtbot.waitSignal(new_shell.executed): new_shell.execute('a = 10') - assert new_client.id_ == dict( - int_id='1', str_id='B', str_env_name='' - ) + assert new_client.id_ == dict(int_id='1', str_id='B') assert shell.get_value('a') == new_shell.get_value('a') From 86ef33cb99641985464c2705d0d968c16e9cdd45 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 18 Apr 2023 14:15:22 -0500 Subject: [PATCH 25/27] Add test and remove extra parenthesis from client name method --- spyder/plugins/ipythonconsole/plugin.py | 10 +++++-- .../plugins/ipythonconsole/tests/conftest.py | 19 ++++++++++-- .../tests/test_ipythonconsole.py | 30 +++++++++++++++++++ .../plugins/ipythonconsole/widgets/client.py | 6 ++-- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index bc2b7a1f249..107bb9e5efe 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -523,7 +523,8 @@ def rename_client_tab(self, client, given_name): self.get_widget().rename_client_tab(client, given_name) def create_new_client(self, give_focus=True, filename='', is_cython=False, - is_pylab=False, is_sympy=False, given_name=None): + is_pylab=False, is_sympy=False, given_name=None, + path_to_custom_interpreter=None): """ Create a new client. @@ -546,6 +547,10 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, given_name : str, optional Initial name displayed in the tab of the client. The default is None. + path_to_custom_interpreter : str, optional + Path to a custom interpreter the client should use regardless of + the interpreter selected in the preferences. + The default is None. Returns ------- @@ -557,7 +562,8 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, is_cython=is_cython, is_pylab=is_pylab, is_sympy=is_sympy, - given_name=given_name) + given_name=given_name, + path_to_custom_interpreter=path_to_custom_interpreter) def create_client_for_file(self, filename, is_cython=False): """ diff --git a/spyder/plugins/ipythonconsole/tests/conftest.py b/spyder/plugins/ipythonconsole/tests/conftest.py index 42059b41f70..fca37597a80 100644 --- a/spyder/plugins/ipythonconsole/tests/conftest.py +++ b/spyder/plugins/ipythonconsole/tests/conftest.py @@ -156,6 +156,15 @@ def __getattr__(self, attr): cython_client = request.node.get_closest_marker('cython_client') is_cython = True if cython_client else False + # Start a specific env client if requested + environment_client = request.node.get_closest_marker( + 'environment_client') + given_name = None + path_to_custom_interpreter = None + if environment_client: + given_name = 'spytest-ž' + path_to_custom_interpreter = get_conda_test_env()[1] + # Use an external interpreter if requested external_interpreter = request.node.get_closest_marker( 'external_interpreter') @@ -197,9 +206,13 @@ def get_plugin(name): debugger.on_ipython_console_available() console.on_initialize() console._register() - console.create_new_client(is_pylab=is_pylab, - is_sympy=is_sympy, - is_cython=is_cython) + console.create_new_client( + is_pylab=is_pylab, + is_sympy=is_sympy, + is_cython=is_cython, + given_name=given_name, + path_to_custom_interpreter=path_to_custom_interpreter + ) window.setCentralWidget(console.get_widget()) # Set exclamation mark to True diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 2f13af8c46d..3509f2dbe85 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -249,6 +249,36 @@ def test_cython_client(ipyconsole, qtbot): assert 'Error' not in control.toPlainText() +@flaky(max_runs=3) +@pytest.mark.order(1) +@pytest.mark.environment_client +@pytest.mark.skipif(not is_anaconda(), reason='Only works with Anaconda') +@pytest.mark.skipif(not running_in_ci(), reason='Only works on CIs') +@pytest.mark.skipif(not os.name == 'nt', reason='Works reliably on Windows') +def test_environment_client(ipyconsole, qtbot): + """ + Test that when creating console for a specific environment the conda + environment associated with the external interpreter + is activated before a kernel is created for it. + """ + # Wait until the window is fully up + shell = ipyconsole.get_current_shellwidget() + + # Check console name + client = ipyconsole.get_current_client() + client.get_name() == "spytest-ž 1/A" + + # Get conda activation environment variable + with qtbot.waitSignal(shell.executed): + shell.execute( + "import os; conda_prefix = os.environ.get('CONDA_PREFIX')" + ) + + expected_output = get_conda_test_env()[0].replace('\\', '/') + output = shell.get_value('conda_prefix').replace('\\', '/') + assert expected_output == output + + @flaky(max_runs=3) def test_tab_rename_for_slaves(ipyconsole, qtbot): """Test slave clients are renamed correctly.""" diff --git a/spyder/plugins/ipythonconsole/widgets/client.py b/spyder/plugins/ipythonconsole/widgets/client.py index 9d032202679..9a1a479b8cb 100644 --- a/spyder/plugins/ipythonconsole/widgets/client.py +++ b/spyder/plugins/ipythonconsole/widgets/client.py @@ -518,14 +518,14 @@ def get_name(self): else: name = self.hostname # Adding id to name - client_id = (self.id_['int_id'] + u'/' + self.id_['str_id']) + client_id = self.id_['int_id'] + u'/' + self.id_['str_id'] name = name + u' ' + client_id elif (self.given_name in ["Pylab", "SymPy", "Cython"] or self.forcing_custom_interpreter): - client_id = (self.id_['int_id'] + u'/' + self.id_['str_id']) + client_id = self.id_['int_id'] + u'/' + self.id_['str_id'] name = self.given_name + u' ' + client_id else: - name = (self.given_name + u'/' + self.id_['str_id']) + name = self.given_name + u'/' + self.id_['str_id'] return name def get_control(self): From 9cf998f8ef484bb4a20233f81f1f6440cceb296c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Thu, 20 Apr 2023 13:39:05 -0500 Subject: [PATCH 26/27] Apply suggestions from code review Co-authored-by: Carlos Cordoba --- spyder/plugins/ipythonconsole/plugin.py | 2 +- .../tests/test_ipythonconsole.py | 5 ++--- .../ipythonconsole/utils/kernelspec.py | 9 ++++++--- .../ipythonconsole/widgets/main_widget.py | 20 ++++++++++++------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index 107bb9e5efe..5bc1fb10765 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -549,7 +549,7 @@ def create_new_client(self, give_focus=True, filename='', is_cython=False, The default is None. path_to_custom_interpreter : str, optional Path to a custom interpreter the client should use regardless of - the interpreter selected in the preferences. + the interpreter selected in Spyder Preferences. The default is None. Returns diff --git a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py index 3509f2dbe85..644ac0501a9 100644 --- a/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py +++ b/spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py @@ -257,9 +257,8 @@ def test_cython_client(ipyconsole, qtbot): @pytest.mark.skipif(not os.name == 'nt', reason='Works reliably on Windows') def test_environment_client(ipyconsole, qtbot): """ - Test that when creating console for a specific environment the conda - environment associated with the external interpreter - is activated before a kernel is created for it. + Test that when creating a console for a specific conda environment, the + environment is activated before a kernel is created for it. """ # Wait until the window is fully up shell = ipyconsole.get_current_shellwidget() diff --git a/spyder/plugins/ipythonconsole/utils/kernelspec.py b/spyder/plugins/ipythonconsole/utils/kernelspec.py index 4208d3daf14..014f0bba631 100644 --- a/spyder/plugins/ipythonconsole/utils/kernelspec.py +++ b/spyder/plugins/ipythonconsole/utils/kernelspec.py @@ -112,8 +112,10 @@ def __init__(self, is_cython=False, is_pylab=False, def argv(self): """Command to start kernels""" # Python interpreter used to start kernels - if (self.get_conf('default', section='main_interpreter') and - not self.path_to_custom_interpreter): + if ( + self.get_conf('default', section='main_interpreter') + and not self.path_to_custom_interpreter + ): pyexec = get_python_executable() else: pyexec = self.get_conf('executable', section='main_interpreter') @@ -190,7 +192,8 @@ def env(self): # Environment variables that we need to pass to the kernel env_vars.update({ - 'SPY_EXTERNAL_INTERPRETER': not default_interpreter or self.path_to_custom_interpreter, + 'SPY_EXTERNAL_INTERPRETER': (not default_interpreter + or self.path_to_custom_interpreter), 'SPY_UMR_ENABLED': self.get_conf( 'umr/enabled', section='main_interpreter'), 'SPY_UMR_VERBOSE': self.get_conf( diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index c6a7b02e041..2c71b9149b1 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -503,7 +503,8 @@ def setup(self): self.console_environment_menu = self.create_menu( IPythonConsoleWidgetOptionsMenus.EnvironmentConsoles, - _('New console in environment')) + _('New console in environment') + ) self.console_environment_menu.setStyleSheet( "QMenu { menu-scrollable: 1; }" ) @@ -562,8 +563,10 @@ def setup(self): icon=self.create_icon('ipython_console'), triggered=self.create_cython_client, ) + self.console_environment_menu.aboutToShow.connect( self.update_environment_menu) + for item in [ create_pylab_action, create_sympy_action, @@ -684,7 +687,8 @@ def get_envs(self): def update_envs(self, worker, output, error): """Update the list of environments in the system.""" - self.envs.update(**output) + if output is not None: + self.envs.update(**output) def update_environment_menu(self): """ @@ -1336,8 +1340,10 @@ def config_options(self): def interpreter_versions(self, path_to_custom_interpreter=None): """Python and IPython versions used by clients""" - if (self.get_conf('default', section='main_interpreter') - and not path_to_custom_interpreter): + if ( + self.get_conf('default', section='main_interpreter') + and not path_to_custom_interpreter + ): from IPython.core import release versions = dict( python_version=sys.version, @@ -1555,9 +1561,9 @@ def create_cython_client(self): self.create_new_client(is_cython=True, given_name="Cython") def create_environment_client( - self, environment, path_to_custom_interpreter - ): - """Force creation of Environment client.""" + self, environment, path_to_custom_interpreter + ): + """Create a client for a Python environment.""" self.create_new_client( given_name=environment, path_to_custom_interpreter=path_to_custom_interpreter From cc0b08db72477756c7ed05700bb67fbfcb36516b Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 20 Apr 2023 14:03:25 -0500 Subject: [PATCH 27/27] Update stylesheet syntax and imports order --- spyder/plugins/ipythonconsole/widgets/main_widget.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spyder/plugins/ipythonconsole/widgets/main_widget.py b/spyder/plugins/ipythonconsole/widgets/main_widget.py index 2c71b9149b1..3000f438472 100644 --- a/spyder/plugins/ipythonconsole/widgets/main_widget.py +++ b/spyder/plugins/ipythonconsole/widgets/main_widget.py @@ -41,13 +41,13 @@ KernelConnectionDialog, PageControlWidget) from spyder.plugins.ipythonconsole.widgets.mixins import CachedKernelMixin from spyder.utils import encoding, programs, sourcecode +from spyder.utils.envs import get_list_envs from spyder.utils.misc import get_error_match, remove_backslashes from spyder.utils.palette import QStylePalette +from spyder.utils.workers import WorkerManager from spyder.widgets.browser import FrameWebView from spyder.widgets.findreplace import FindReplace from spyder.widgets.tabs import Tabs -from spyder.utils.workers import WorkerManager -from spyder.utils.envs import get_list_envs # Logging @@ -505,9 +505,9 @@ def setup(self): IPythonConsoleWidgetOptionsMenus.EnvironmentConsoles, _('New console in environment') ) - self.console_environment_menu.setStyleSheet( - "QMenu { menu-scrollable: 1; }" - ) + stylesheet = qstylizer.style.StyleSheet() + stylesheet["QMenu"]["menu-scrollable"].setValue("1") + self.console_environment_menu.setStyleSheet(stylesheet.toString()) self.special_console_menu = self.create_menu( IPythonConsoleWidgetOptionsMenus.SpecialConsoles,