Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add menu to use specific environment interpreter for a new console instance #20421

Merged
merged 34 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
013e591
Add menu consoles environment
jsbautista Jan 23, 2023
23e7e87
Add menu consoles environment
jsbautista Jan 23, 2023
631bf20
Add updates to console environments
jsbautista Jan 24, 2023
e49bd1c
Change text in Preferences
jsbautista Jan 24, 2023
c79d695
Add updates to console environments
jsbautista Feb 6, 2023
5e890e1
Add updates to console environments
jsbautista Feb 7, 2023
b14818b
Merge branch 'master' into masterConsoleEnvironment
jsbautista Feb 7, 2023
a62410f
Apply suggestions from code review
jsbautista Feb 13, 2023
d33be31
Apply suggestions from code review
jsbautista Feb 13, 2023
ab1d363
Apply suggestions from code review
jsbautista Feb 17, 2023
d946fee
Apply suggestions from code review
jsbautista Feb 27, 2023
cde591f
Apply suggestions from code review
jsbautista Mar 1, 2023
a347fc5
Apply suggestions from code review
jsbautista Mar 6, 2023
a7c5662
Merge branch 'masterConsoleEnvironment' of https://github.com/jsbauti…
jsbautista Mar 6, 2023
8f940b1
Apply suggestions from code review
jsbautista Mar 6, 2023
dc30583
Apply suggestions
jsbautista Mar 8, 2023
0251147
Merge branch 'master' into masterConsoleEnvironment
jsbautista Mar 8, 2023
ef84f78
Apply suggestions
jsbautista Mar 8, 2023
0aa1662
Apply suggestions
jsbautista Mar 12, 2023
867b8fe
Update IPython console plugin tests
dalthviz Mar 14, 2023
4ba249a
Fix IPython console mainwindow test
dalthviz Mar 14, 2023
4bb02d1
Fix some code style issues. Refactor getting envs information
dalthviz Mar 15, 2023
2c04843
Restore some removed blank lines. Update envs module docstring
dalthviz Mar 15, 2023
560853a
Merge branch 'master' into masterConsoleEnvironment
dalthviz Mar 22, 2023
ab339fe
Merge branch 'master' into masterConsoleEnvironment
dalthviz Apr 12, 2023
090a579
Merge branch 'master' into masterConsoleEnvironment
dalthviz Apr 13, 2023
373ad25
Simplify naming approach for the consoles tabs when created from the …
dalthviz Apr 14, 2023
f3164cb
Fix IPython console test
dalthviz Apr 17, 2023
778d6cf
Add style from QMenu to not expand more than one column
dalthviz Apr 17, 2023
d34bae8
Merge remote-tracking branch 'upstream/master' into masterConsoleEnvi…
dalthviz Apr 17, 2023
fc3afdf
Fix IPython console test
dalthviz Apr 17, 2023
86ef33c
Add test and remove extra parenthesis from client name method
dalthviz Apr 18, 2023
9cf998f
Apply suggestions from code review
dalthviz Apr 20, 2023
cc0b08d
Update stylesheet syntax and imports order
dalthviz Apr 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions spyder/plugins/ipythonconsole/utils/kernelspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,13 @@ class SpyderKernelSpec(KernelSpec, SpyderConfigurationAccessor):
CONF_SECTION = 'ipython_console'

def __init__(self, is_cython=False, is_pylab=False,
is_sympy=False, **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.path_to_custom_interpreter = path_to_custom_interpreter
self.display_name = 'Python 3 (Spyder)'
self.language = 'python3'
self.resource_dir = ''
Expand All @@ -113,10 +114,13 @@ 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'):
if (self.get_conf('default', section='main_interpreter') and
not self.path_to_custom_interpreter):
dalthviz marked this conversation as resolved.
Show resolved Hide resolved
pyexec = get_python_executable()
else:
pyexec = self.get_conf('executable', section='main_interpreter')
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(
Expand Down Expand Up @@ -187,7 +191,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': self.path_to_custom_interpreter,
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
'SPY_UMR_ENABLED': self.get_conf(
'umr/enabled', section='main_interpreter'),
'SPY_UMR_VERBOSE': self.get_conf(
Expand Down
107 changes: 95 additions & 12 deletions spyder/plugins/ipythonconsole/widgets/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +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.pyenv import get_list_pyenv_envs


# Localization and logging
Expand All @@ -64,6 +67,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'
Expand Down Expand Up @@ -94,6 +98,7 @@ class IPythonConsoleWidgetActions:
class IPythonConsoleWidgetOptionsMenus:
SpecialConsoles = 'special_consoles_submenu'
Documentation = 'documentation_submenu'
EnvironmentConsoles = 'environment_consoles_submenu'


class IPythonConsoleWidgetOptionsMenuSections:
Expand Down Expand Up @@ -267,7 +272,10 @@ 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

# Disable infowidget if requested by the user
self.enable_infowidget = True
Expand Down Expand Up @@ -351,6 +359,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()
Expand Down Expand Up @@ -488,12 +502,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'))
dalthviz marked this conversation as resolved.
Show resolved Hide resolved

self.special_console_menu = self.create_menu(
IPythonConsoleWidgetOptionsMenus.SpecialConsoles,
_('Special consoles'))
_('New special console'))

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(
Expand Down Expand Up @@ -541,7 +561,8 @@ def setup(self):
icon=self.create_icon('ipython_console'),
triggered=self.create_cython_client,
)

self.console_environment_menu.aboutToShow.connect(
self.update_environment_menu)
dalthviz marked this conversation as resolved.
Show resolved Hide resolved
for item in [
create_pylab_action,
create_sympy_action,
Expand Down Expand Up @@ -579,6 +600,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(
Expand Down Expand Up @@ -650,6 +672,57 @@ 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.
"""
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)
dalthviz marked this conversation as resolved.
Show resolved Hide resolved

def update_environment_menu(self):
"""Update context menu entries to select specific interpreter to launch a console."""
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(
dalthviz marked this conversation as resolved.
Show resolved Hide resolved
name=name,
text=item + ' ('+environment_consoles_names[item][1]+')',
jsbautista marked this conversation as resolved.
Show resolved Hide resolved
icon=self.create_icon('ipython_console'),
triggered=lambda checked, environment=name,
path=path_to_environment:
self.create_environment_client(
path_to_custom_interpreter=path, environment=environment),
)
environment_consoles.append(action)
for item in environment_consoles:
self.add_item_to_menu(
item,
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):
Expand Down Expand Up @@ -1269,9 +1342,10 @@ def config_options(self):
cfg._merge(spy_cfg)
return cfg

def interpreter_versions(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'):
if (self.get_conf('default', section='main_interpreter')
and not path_to_custom_interpreter):
dalthviz marked this conversation as resolved.
Show resolved Hide resolved
from IPython.core import release
versions = dict(
python_version=sys.version,
Expand All @@ -1281,6 +1355,8 @@ def interpreter_versions(self):
import subprocess
versions = {}
pyexec = self.get_conf('executable', section='main_interpreter')
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)"'
Expand Down Expand Up @@ -1346,15 +1422,17 @@ 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):
cache=True, initial_cwd=None, environment='A',
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_id=environment)
dalthviz marked this conversation as resolved.
Show resolved Hide resolved

client = ClientWidget(
self,
Expand All @@ -1363,24 +1441,24 @@ 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(
path_to_custom_interpreter),
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)

dalthviz marked this conversation as resolved.
Show resolved Hide resolved
# Create new kernel
kernel_spec = SpyderKernelSpec(
is_cython=is_cython,
is_pylab=is_pylab,
is_sympy=is_sympy
is_sympy=is_sympy,
path_to_custom_interpreter=path_to_custom_interpreter
)

try:
Expand Down Expand Up @@ -1481,6 +1559,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=''):
"""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):
"""Create a client with its cwd pointing to path."""
Expand Down Expand Up @@ -1528,7 +1612,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)

Expand Down
4 changes: 2 additions & 2 deletions spyder/plugins/maininterpreter/confpage.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down