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: Modernize PYTHONPATH Manager configuration #18778

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
145 changes: 66 additions & 79 deletions spyder/app/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
#==============================================================================
qInstallMessageHandler(qt_message_handler)


#==============================================================================
# Main Window
#==============================================================================
Expand All @@ -128,8 +129,6 @@ class MainWindow(QMainWindow):
QMainWindow.AllowTabbedDocks | QMainWindow.AllowNestedDocks |
QMainWindow.AnimatedDocks
)
SPYDER_PATH = get_conf_path('path')
SPYDER_NOT_ACTIVE_PATH = get_conf_path('not_active_path')
DEFAULT_LAYOUTS = 4
INITIAL_CWD = getcwd_or_home()

Expand Down Expand Up @@ -570,10 +569,9 @@ def signal_handler(signum, frame=None):
self.shortcut_queue = []

# Handle Spyder path
self.path = ()
self.not_active_path = ()
self.project_path = ()
self.old_path = OrderedDict()
self._path_manager = None
self.load_python_path() # TODO: Remove on later release

# New API
self._APPLICATION_TOOLBARS = OrderedDict()
Expand Down Expand Up @@ -760,7 +758,7 @@ def setup(self):
from spyder.plugins.help.utils.sphinxify import CSS_PATH, DARK_CSS_PATH
logger.info("*** Start of MainWindow setup ***")
logger.info("Updating PYTHONPATH")
path_dict = self.get_spyder_pythonpath_dict()
path_dict = self.get_spyder_pythonpath()
self.update_python_path(path_dict)

logger.info("Applying theme configuration...")
Expand Down Expand Up @@ -1660,84 +1658,86 @@ def open_external_file(self, fname):
.format(fpath=osp.normpath(fpath), fname=fname)
)

# --- Path Manager
# ------------------------------------------------------------------------
# ---- Path Manager
def load_python_path(self):
"""Load path stored in Spyder configuration folder."""
if osp.isfile(self.SPYDER_PATH):
with open(self.SPYDER_PATH, 'r', encoding='utf-8') as f:
path = f.read().splitlines()
self.path = tuple(name for name in path if osp.isdir(name))

if osp.isfile(self.SPYDER_NOT_ACTIVE_PATH):
with open(self.SPYDER_NOT_ACTIVE_PATH, 'r',
encoding='utf-8') as f:
not_active_path = f.read().splitlines()
self.not_active_path = tuple(name for name in not_active_path
if osp.isdir(name))

def save_python_path(self, new_path_dict):
"""
Save path in Spyder configuration folder.

`new_path_dict` is an OrderedDict that has the new paths as keys and
the state as values. The state is `True` for active and `False` for
inactive.
"""
path = [p for p in new_path_dict]
not_active_path = [p for p in new_path_dict if not new_path_dict[p]]
try:
encoding.writelines(path, self.SPYDER_PATH)
encoding.writelines(not_active_path, self.SPYDER_NOT_ACTIVE_PATH)
except EnvironmentError as e:
logger.error(str(e))
CONF.set('main', 'spyder_pythonpath', self.get_spyder_pythonpath())
Load paths stored in Spyder configuration folder, add them to the
modern configuration, and remove the obsolete files.

def get_spyder_pythonpath_dict(self):
TODO: Remove on later release
"""
Return Spyder PYTHONPATH.
SPYDER_PATH = get_conf_path('path')
SPYDER_NOT_ACTIVE_PATH = get_conf_path('not_active_path')

The returned ordered dictionary has the paths as keys and the state
as values. The state is `True` for active and `False` for inactive.
path_dict = OrderedDict()
if osp.isfile(SPYDER_PATH):
with open(SPYDER_PATH, 'r', encoding='utf-8') as f:
paths = f.read().splitlines()
for path in paths:
if osp.isdir(path):
path_dict[path] = True
os.remove(SPYDER_PATH) # No longer use file
logger.info(f"Removed obsolete '{SPYDER_PATH}'")

if osp.isfile(SPYDER_NOT_ACTIVE_PATH):
with open(SPYDER_NOT_ACTIVE_PATH, 'r', encoding='utf-8') as f:
paths = f.read().splitlines()
for path in paths:
if osp.isdir(path):
path_dict[path] = False
os.remove(SPYDER_NOT_ACTIVE_PATH) # No longer use file
logger.info(f"Removed obsolete '{SPYDER_NOT_ACTIVE_PATH}'")

if path_dict:
self.update_python_path(path_dict)

Example:
OrderedDict([('/some/path, True), ('/some/other/path, False)])
def get_spyder_pythonpath(self):
"""
self.load_python_path()

path_dict = OrderedDict()
for path in self.path:
path_dict[path] = path not in self.not_active_path
Return Spyder PYTHONPATH, including project paths, as ordered
dictionary.

for path in self.project_path:
The returned dictionary has the paths as keys and the state as values.
The state is `True` for active and `False` for inactive.
"""
path_dict = OrderedDict(CONF.get('pythonpath_manager', 'paths'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shows to me that what you want is a new plugin because you're adding a new section in our config system to handle this: pythonpath_manager.

for path in self.get_project_paths():
path_dict[path] = True

return path_dict

def get_spyder_pythonpath(self):
def get_project_paths(self):
"""Return project paths as tuple"""
projects = self.get_plugin(Plugins.Projects, error=False)
proj_paths = ()
if projects:
proj_paths = tuple(projects.get_pythonpath())

return proj_paths

def get_spyder_active_pythonpath(self):
"""
Return Spyder PYTHONPATH.
Return Spyder PYTHONPATH, including project paths, as list of active
paths.
"""
path_dict = self.get_spyder_pythonpath_dict()
path_dict = self.get_spyder_pythonpath()
path = [k for k, v in path_dict.items() if v]
return path

def update_python_path(self, new_path_dict):
"""Update python path on Spyder interpreter and kernels."""
# Load previous path
path_dict = self.get_spyder_pythonpath_dict()

# Save path
if path_dict != new_path_dict:
# It doesn't include the project_path
self.save_python_path(new_path_dict)
def update_python_path(self, new_path_dict=None):
"""
Update python path in configuration and on Spyder interpreter and
kernels.
"""
if new_path_dict is not None:
CONF.set('pythonpath_manager', 'paths', dict(new_path_dict))

# Load new path
new_path_dict_p = self.get_spyder_pythonpath_dict() # Includes project
new_path = self.get_spyder_pythonpath()

# Any plugin that needs to do some work based on this signal should
# connect to it on plugin registration
self.sig_pythonpath_changed.emit(path_dict, new_path_dict_p)
self.sig_pythonpath_changed.emit(self.old_path, new_path)

self.old_path = new_path

@Slot()
def show_path_manager(self):
Expand All @@ -1748,13 +1748,10 @@ def _dialog_finished(result_code):

if self._path_manager is None:
from spyder.widgets.pathmanager import PathManager
projects = self.get_plugin(Plugins.Projects, error=False)
read_only_path = ()
if projects:
read_only_path = tuple(projects.get_pythonpath())

dialog = PathManager(self, self.path, read_only_path,
self.not_active_path, sync=True)
paths = OrderedDict(CONF.get('pythonpath_manager', 'paths'))
proj_paths = self.get_project_paths()
dialog = PathManager(self, paths=paths, read_only_paths=proj_paths)
self._path_manager = dialog
dialog.sig_path_changed.connect(self.update_python_path)
dialog.redirect_stdio.connect(self.redirect_internalshell_stdio)
Expand All @@ -1766,16 +1763,6 @@ def _dialog_finished(result_code):
self._path_manager.raise_()
self._path_manager.setFocus()

def pythonpath_changed(self):
"""Project's PYTHONPATH contribution has changed."""
projects = self.get_plugin(Plugins.Projects, error=False)

self.project_path = ()
if projects:
self.project_path = tuple(projects.get_pythonpath())
path_dict = self.get_spyder_pythonpath_dict()
self.update_python_path(path_dict)

#---- Preferences
def apply_settings(self):
"""Apply main window settings."""
Expand Down