# -*- coding: utf-8 -*- """Core structures for third-party application descriptions.""" __all__ = ['BaseApp', 'BaseWebApp', 'BaseInstallableApp'] import abc import typing from anaconda_navigator.utils.constants import AppType if typing.TYPE_CHECKING: import typing_extensions from anaconda_navigator.api import process from anaconda_navigator.config import user as user_config from . import detectors class BaseApp(metaclass=abc.ABCMeta): # pylint: disable=too-few-public-methods,too-many-instance-attributes """ Root description of the application. :param app_type: Type of the application (web application, installable, etc.). :param app_name: Alias of the application in a package-naming format. :param display_name: Name of the application to show in home tile. :param description: Description of the application to show in home tile. :param image_path: Application icon to show in home tile. :param config: Application configuration. """ def __init__( # pylint: disable=too-many-arguments self, app_type: AppType, app_name: str, display_name: str, description: str, image_path: str, is_available: bool, non_conda: bool, config: 'user_config.UserConfig', ) -> None: """Initialize new :class:`~BaseApp` instance.""" self.app_type: 'typing_extensions.Final[AppType]' = app_type self.app_name: 'typing_extensions.Final[str]' = app_name self.display_name: 'typing_extensions.Final[str]' = display_name self.description: 'typing_extensions.Final[str]' = description self.image_path: 'typing_extensions.Final[str]' = image_path self.non_conda: 'typing_extensions.Final[bool]' = non_conda self.config: 'typing_extensions.Final[user_config.UserConfig]' = config self.is_available: 'typing_extensions.Final[bool]' = is_available class BaseWebApp(BaseApp, metaclass=abc.ABCMeta): # pylint: disable=too-few-public-methods """ Common base for all external web applications. Most of the params reused from the :class:`~BaseApp`. :param url: Web page to open for the application. """ def __init__( # pylint: disable=too-many-arguments self, app_name: str, display_name: str, description: str, image_path: str, url: str, is_available: bool, config: 'user_config.UserConfig', ) -> None: """Initialize new :class:`~BaseWebApp` instance.""" super().__init__( app_type=AppType.WEB, app_name=app_name, display_name=display_name, description=description, image_path=image_path, is_available=is_available, non_conda=True, config=config, ) self.url: 'typing_extensions.Final[str]' = url class BaseInstallableApp(BaseApp, metaclass=abc.ABCMeta): """ Common base for all external installable applications. Most of the params reused from the :class:`~BaseApp`. :param detector: Detector of the application location. :param process_api: API to spawn new processes (for extensions installation etc.). """ def __init__( # pylint: disable=too-many-arguments self, app_name: str, display_name: str, description: str, image_path: str, detector: 'detectors.Source', is_available: bool, process_api: 'process.WorkerManager', config: 'user_config.UserConfig', extra_arguments: typing.Sequence[typing.Any], ) -> None: """Initialize new :class:`~BaseInstallableApp` instance.""" super().__init__( app_type=AppType.INSTALLABLE, app_name=app_name, display_name=display_name, description=description, image_path=image_path, is_available=is_available, non_conda=True, config=config, ) self.extra_arguments: 'typing_extensions.Final[typing.Sequence[typing.Any]]' = extra_arguments self._process_api: 'typing_extensions.Final[process.WorkerManager]' = process_api location: 'typing.Optional[detectors.DetectedApplication]' for location in detector(): if location.complete: break else: location = None self.__location: 'typing_extensions.Final[typing.Optional[detectors.DetectedApplication]]' = location self.config.set('main', f'{self.app_name}_path', self.root or '') @property def executable(self) -> typing.Optional[str]: """Return executable command.""" if self.__location is None: return None from anaconda_navigator.utils import launch # pylint: disable=import-outside-toplevel return launch.safe_argument(self.__location.executable) @property def is_installation_enabled(self) -> bool: """Application can be installed.""" return hasattr(self, 'install') @property def is_installed(self) -> bool: """Return whether the app is installed.""" return bool(self.executable) @property def root(self) -> typing.Optional[str]: """Return application root directory.""" if self.__location is None: return None return self.__location.root @property def version(self) -> typing.Optional[str]: """Version of the installed application.""" if self.__location is None: return None return self.__location.version @property def versions(self) -> typing.Sequence[str]: """List of available application versions.""" if self.__location is None: return [] return [self.__location.version] @abc.abstractmethod def update_config(self, prefix: str) -> None: """Update user config to use selected Python prefix interpreter.""" @abc.abstractmethod def install_extensions(self) -> 'process.ProcessWorker': """Install app extensions."""