Skip to content

Commit

Permalink
Merge pull request #22079 from ccordoba12/remote-client-ui
Browse files Browse the repository at this point in the history
PR: Add UI for the remote client plugin
  • Loading branch information
ccordoba12 committed May 15, 2024
2 parents 923ac95 + d07d9f4 commit b4d28aa
Show file tree
Hide file tree
Showing 37 changed files with 2,637 additions and 251 deletions.
49 changes: 34 additions & 15 deletions spyder/api/config/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ class SpyderConfigurationAccessor:
# config system.
CONF_SECTION = None

def get_conf(self,
option: ConfigurationKey,
default: Union[NoDefault, BasicTypes] = NoDefault,
section: Optional[str] = None):
def get_conf(
self,
option: ConfigurationKey,
default: Union[NoDefault, BasicTypes] = NoDefault,
section: Optional[str] = None,
secure: Optional[bool] = False,
):
"""
Get an option from the Spyder configuration system.
Expand All @@ -55,6 +58,9 @@ def get_conf(self,
section: str
Section in the configuration system, e.g. `shortcuts`. If None,
then the value of `CONF_SECTION` is used.
secure: bool
If True, the option will be retrieved securely using the `keyring`
Python package.
Returns
-------
Expand All @@ -73,7 +79,7 @@ def get_conf(self,
'class attribute!'
)

return CONF.get(section, option, default)
return CONF.get(section, option, default, secure)

def get_conf_options(self, section: Optional[str] = None):
"""
Expand Down Expand Up @@ -103,11 +109,14 @@ def get_conf_options(self, section: Optional[str] = None):
)
return CONF.options(section)

def set_conf(self,
option: ConfigurationKey,
value: BasicTypes,
section: Optional[str] = None,
recursive_notification: bool = True):
def set_conf(
self,
option: ConfigurationKey,
value: BasicTypes,
section: Optional[str] = None,
recursive_notification: bool = True,
secure: Optional[bool] = False,
):
"""
Set an option in the Spyder configuration system.
Expand All @@ -127,6 +136,9 @@ def set_conf(self,
changes, then the observers for section `sec` are notified.
Likewise, if the option `(a, b, c)` changes, then observers for
`(a, b, c)`, `(a, b)` and a are notified as well.
secure: bool
If True, the option will be saved securely using the `keyring`
Python package.
"""
section = self.CONF_SECTION if section is None else section
if section is None:
Expand All @@ -138,12 +150,16 @@ def set_conf(self,
section,
option,
value,
recursive_notification=recursive_notification
recursive_notification=recursive_notification,
secure=secure,
)

def remove_conf(self,
option: ConfigurationKey,
section: Optional[str] = None):
def remove_conf(
self,
option: ConfigurationKey,
section: Optional[str] = None,
secure: Optional[str] = False,
):
"""
Remove an option in the Spyder configuration system.
Expand All @@ -154,14 +170,17 @@ def remove_conf(self,
section: Optional[str]
Section in the configuration system, e.g. `shortcuts`. If None,
then the value of `CONF_SECTION` is used.
secure: bool
If True, the option will be removed securely using the `keyring`
Python package.
"""
section = self.CONF_SECTION if section is None else section
if section is None:
raise AttributeError(
'A SpyderConfigurationAccessor must define a `CONF_SECTION` '
'class attribute!'
)
CONF.remove_option(section, option)
CONF.remove_option(section, option, secure)

def get_conf_default(self,
option: ConfigurationKey,
Expand Down
36 changes: 28 additions & 8 deletions spyder/api/plugins/new_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ def get_dockable_plugins(self):
dockable_plugins_required.append(plugin_instance)
return dockable_plugins_required

def get_conf(self, option, default=NoDefault, section=None):
def get_conf(self, option, default=NoDefault, section=None, secure=False):
"""
Get an option from Spyder configuration system.
Expand All @@ -482,6 +482,9 @@ def get_conf(self, option, default=NoDefault, section=None):
Python object.
section: str
Section in the configuration system, e.g. `shortcuts`.
secure: bool
If True, the option will be retrieved securely using the `keyring`
Python package.
Returns
-------
Expand All @@ -495,12 +498,18 @@ def get_conf(self, option, default=NoDefault, section=None):
'A spyder plugin must define a `CONF_SECTION` class '
'attribute!'
)
return self._conf.get(section, option, default)
return self._conf.get(section, option, default, secure=secure)

@Slot(str, object)
@Slot(str, object, str)
def set_conf(self, option, value, section=None,
recursive_notification=True):
def set_conf(
self,
option,
value,
section=None,
recursive_notification=True,
secure=False,
):
"""
Set an option in Spyder configuration system.
Expand All @@ -520,6 +529,9 @@ def set_conf(self, option, value, section=None,
changes, then the observers for section `sec` are notified.
Likewise, if the option `(a, b, c)` changes, then observers for
`(a, b, c)`, `(a, b)` and a are notified as well.
secure: bool
If True, the option will be saved securely using the `keyring`
Python package.
"""
if self._conf is not None:
section = self.CONF_SECTION if section is None else section
Expand All @@ -529,11 +541,16 @@ def set_conf(self, option, value, section=None,
'attribute!'
)

self._conf.set(section, option, value,
recursive_notification=recursive_notification)
self._conf.set(
section,
option,
value,
recursive_notification=recursive_notification,
secure=secure,
)
self.apply_conf({option}, False)

def remove_conf(self, option, section=None):
def remove_conf(self, option, section=None, secure=False):
"""
Delete an option in the Spyder configuration system.
Expand All @@ -543,6 +560,9 @@ def remove_conf(self, option, section=None):
Name of the option, either a string or a tuple of strings.
section: str
Section in the configuration system.
secure: bool
If True, the option will be removed securely using the `keyring`
Python package.
"""
if self._conf is not None:
section = self.CONF_SECTION if section is None else section
Expand All @@ -552,7 +572,7 @@ def remove_conf(self, option, section=None):
'attribute!'
)

self._conf.remove_option(section, option)
self._conf.remove_option(section, option, secure=secure)
self.apply_conf({option}, False)

def apply_conf(self, options_set, notify=True):
Expand Down
3 changes: 2 additions & 1 deletion spyder/api/shellconnect/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@ def add_errored_shellwidget(self, shellwidget):
if shellwidget_id in self._shellwidgets:
self._shellwidgets.pop(shellwidget_id)

remote = shellwidget.ipyclient.server_id
widget = PaneEmptyWidget(
self,
"console-off",
"console-remote-off" if remote else "console-off",
_("No connected console"),
_("The current console failed to start, so there is no "
"content to show here.")
Expand Down
2 changes: 0 additions & 2 deletions spyder/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
"""
API utilities.
"""
import functools
import asyncio


def get_class_values(cls):
Expand Down
74 changes: 61 additions & 13 deletions spyder/config/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
from typing import Any, Dict, List, Optional, Set, Tuple
import weakref

# Third-party imports
import keyring

# Local imports
from spyder.api.utils import PrefixedTuple
from spyder.config.base import (
Expand Down Expand Up @@ -310,10 +313,13 @@ def notify_all_observers(self):
for section in self._observers:
self.notify_section_all_observers(section)

def notify_observers(self,
section: str,
option: ConfigurationKey,
recursive_notification: bool = True):
def notify_observers(
self,
section: str,
option: ConfigurationKey,
recursive_notification: bool = True,
secure: bool = False,
):
"""
Notify observers of a change in the option `option` of configuration
section `section`.
Expand All @@ -331,6 +337,8 @@ def notify_observers(self,
changes, then the observers for section `sec` are notified.
Likewise, if the option `(a, b, c)` changes, then observers for
`(a, b, c)`, `(a, b)` and a are notified as well.
secure: bool
Whether this is a secure option or not.
"""
if recursive_notification:
# Notify to section listeners
Expand All @@ -353,7 +361,7 @@ def notify_observers(self,
if option == '__section':
self._notify_section(section)
else:
value = self.get(section, option)
value = self.get(section, option, secure=secure)
self._notify_option(section, option, value)

def _notify_option(self, section: str, option: ConfigurationKey,
Expand Down Expand Up @@ -470,7 +478,7 @@ def options(self, section):
config = self.get_active_conf(section)
return config.options(section)

def get(self, section, option, default=NoDefault):
def get(self, section, option, default=NoDefault, secure=False):
"""
Get an `option` on a given `section`.
Expand All @@ -497,11 +505,25 @@ def get(self, section, option, default=NoDefault):
if default is NoDefault:
raise cp.NoOptionError(option, section)
else:
value = config.get(section=section, option=option, default=default)
if secure:
logger.debug(
f"Retrieving option {option} with keyring because it "
f"was marked as secure."
)
value = keyring.get_password(section, option)

# This happens when `option` was not actually saved by keyring
if value is None:
value = ""
else:
value = config.get(
section=section, option=option, default=default
)

return value

def set(self, section, option, value, verbose=False, save=True,
recursive_notification=True, notification=True):
recursive_notification=True, notification=True, secure=False):
"""
Set an `option` on a given `section`.
Expand All @@ -525,11 +547,26 @@ def set(self, section, option, value, verbose=False, save=True,
option = base_option

config = self.get_active_conf(section)
config.set(section=section, option=option, value=value,
verbose=verbose, save=save)

if secure:
logger.debug(
f"Saving option {option} with keyring because it was marked "
f"as secure."
)
keyring.set_password(section, option, value)
else:
config.set(
section=section,
option=option,
value=value,
verbose=verbose,
save=save,
)

if notification:
self.notify_observers(
section, original_option, recursive_notification)
section, original_option, recursive_notification, secure
)

def get_default(self, section, option):
"""
Expand Down Expand Up @@ -557,9 +594,10 @@ def remove_section(self, section):
config = self.get_active_conf(section)
config.remove_section(section)

def remove_option(self, section, option):
def remove_option(self, section, option, secure=False):
"""Remove `option` from `section`."""
config = self.get_active_conf(section)

if isinstance(option, tuple):
# The actual option saved in the config
base_option = option[0]
Expand Down Expand Up @@ -588,7 +626,17 @@ def remove_option(self, section, option):
self.set(section, base_option, base_conf)
self.notify_observers(section, base_option)
else:
config.remove_option(section, option)
if secure:
logger.debug(
f"Deleting option {option} with keyring because it was "
f"marked as secure."
)
try:
keyring.delete_password(section, option)
except Exception:
pass
else:
config.remove_option(section, option)

def reset_to_defaults(self, section=None, notification=True):
"""Reset config to Default values."""
Expand Down

0 comments on commit b4d28aa

Please sign in to comment.