# -*- coding: utf-8 -*- # # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # (see spyder/__init__.py for details) """ Projects Plugin API. """ # Standard library imports import os import os.path as osp from collections import OrderedDict # Local imports from spyder.api.exceptions import SpyderAPIError from spyder.api.translations import get_translation from spyder.config.base import get_project_config_folder from spyder.plugins.projects.utils.config import (ProjectMultiConfig, PROJECT_NAME_MAP, PROJECT_DEFAULTS, PROJECT_CONF_VERSION, WORKSPACE, CODESTYLE, ENCODING, VCS) # Localization _ = get_translation("spyder") class BaseProjectType: """ Spyder base project. This base class must not be used directly, but inherited from. It does not assume that python is specific to this project. """ ID = None def __init__(self, root_path, parent_plugin=None): self.plugin = parent_plugin self.root_path = root_path self.open_project_files = [] self.open_non_project_files = [] path = os.path.join(root_path, get_project_config_folder(), 'config') self.config = ProjectMultiConfig( PROJECT_NAME_MAP, path=path, defaults=PROJECT_DEFAULTS, load=True, version=PROJECT_CONF_VERSION, backup=True, raw_mode=True, remove_obsolete=False, ) act_name = self.get_option("project_type") if not act_name: self.set_option("project_type", self.ID) # --- Helpers # ------------------------------------------------------------------------- def get_option(self, option, section=WORKSPACE, default=None): """Get project configuration option.""" return self.config.get(section=section, option=option, default=default) def set_option(self, option, value, section=WORKSPACE): """Set project configuration option.""" self.config.set(section=section, option=option, value=value) def set_recent_files(self, recent_files): """Set a list of files opened by the project.""" processed_recent_files = [] for recent_file in recent_files: if os.path.isfile(recent_file): try: relative_recent_file = os.path.relpath( recent_file, self.root_path) processed_recent_files.append(relative_recent_file) except ValueError: processed_recent_files.append(recent_file) files = list(OrderedDict.fromkeys(processed_recent_files)) self.set_option("recent_files", files) def get_recent_files(self): """Return a list of files opened by the project.""" # Check if recent_files in [main] (Spyder 4) recent_files = self.get_option("recent_files", 'main', []) if recent_files: # Move to [workspace] (Spyder 5) self.config.remove_option('main', 'recent_files') self.set_recent_files(recent_files) else: recent_files = self.get_option("recent_files", default=[]) recent_files = [recent_file if os.path.isabs(recent_file) else os.path.join(self.root_path, recent_file) for recent_file in recent_files] for recent_file in recent_files[:]: if not os.path.isfile(recent_file): recent_files.remove(recent_file) return list(OrderedDict.fromkeys(recent_files)) # --- API # ------------------------------------------------------------------------ @staticmethod def get_name(): """ Provide a human readable version of NAME. """ raise NotImplementedError("Must implement a `get_name` method!") @staticmethod def validate_name(path, name): """ Validate the project's name. Returns ------- tuple The first item (bool) indicates if the name was validated successfully, and the second item (str) indicates the error message, if any. """ return True, "" def create_project(self): """ Create a project and do any additional setup for this project type. Returns ------- tuple The first item (bool) indicates if the project was created successfully, and the second item (str) indicates the error message, if any. """ return False, "A ProjectType must define a `create_project` method!" def open_project(self): """ Open a project and do any additional setup for this project type. Returns ------- tuple The first item (bool) indicates if the project was opened successfully, and the second item (str) indicates the error message, if any. """ return False, "A ProjectType must define an `open_project` method!" def close_project(self): """ Close a project and do any additional setup for this project type. Returns ------- tuple The first item (bool) indicates if the project was closed successfully, and the second item (str) indicates the error message, if any. """ return False, "A ProjectType must define a `close_project` method!" class EmptyProject(BaseProjectType): ID = 'empty-project-type' @staticmethod def get_name(): return _("Empty project") def create_project(self): return True, "" def open_project(self): return True, "" def close_project(self): return True, ""