def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult: if is_url or (not snap.is_installed() and not snapd.is_running()): return SearchResult([], [], 0) snapd_client = SnapdClient(self.logger) apps_found = snapd_client.query(words) res = SearchResult([], [], 0) if apps_found: installed = self.read_installed(disk_loader).installed for app_json in apps_found: already_installed = None if installed: already_installed = [ i for i in installed if i.id == app_json.get('id') ] already_installed = already_installed[ 0] if already_installed else None if already_installed: res.installed.append(already_installed) else: res.new.append(self._map_to_app(app_json, installed=False)) res.total = len(res.installed) + len(res.new) return res
def list_warnings(self, internet_available: bool) -> List[str]: if snap.is_installed(): if not snapd.is_running(): snap_bold = bold('Snap') return [ self.i18n['snap.notification.snapd_unavailable'].format( bold('snapd'), snap_bold), self.i18n['snap.notification.snap.disable'].format( snap_bold, bold('{} > {}'.format( self.i18n['settings'].capitalize(), self.i18n['core.config.tab.types']))) ] elif internet_available: available, output = snap.is_api_available() if not available: self.logger.warning( 'It seems Snap API is not available. Search output: {}' .format(output)) return [ self.i18n['snap.notifications.api.unavailable'].format( bold('Snaps'), bold('Snap')) ]
def downgrade(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher) -> bool: if not snap.is_installed(): watcher.print("'snap' seems not to be installed") return False if not snapd.is_running(): watcher.print("'snapd' seems not to be running") return False return ProcessHandler(watcher).handle_simple( snap.downgrade_and_stream(pkg.name, root_password))[0]
def list_suggestions(self, limit: int, filter_installed: bool) -> List[PackageSuggestion]: res = [] if snapd.is_running(): self.logger.info( 'Downloading suggestions file {}'.format(SUGGESTIONS_FILE)) file = self.http_client.get(SUGGESTIONS_FILE) if not file or not file.text: self.logger.warning( "No suggestion found in {}".format(SUGGESTIONS_FILE)) return res else: self.logger.info('Mapping suggestions') suggestions, threads = [], [] snapd_client = SnapdClient(self.logger) installed = { s['name'].lower() for s in snapd_client.list_all_snaps() } for l in file.text.split('\n'): if l: if limit <= 0 or len(suggestions) < limit: sug = l.strip().split('=') name = sug[1] if not installed or name not in installed: cached_sug = self.suggestions_cache.get(name) if cached_sug: res.append(cached_sug) else: t = Thread(target=self._fill_suggestion, args=(name, SuggestionPriority( int(sug[0])), snapd_client, res)) t.start() threads.append(t) time.sleep(0.001) # to avoid being blocked else: break for t in threads: t.join() res.sort(key=lambda s: s.priority.value, reverse=True) return res
def uninstall(self, pkg: SnapApplication, root_password: str, watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult: if snap.is_installed() and snapd.is_running(): uninstalled = ProcessHandler(watcher).handle_simple( snap.uninstall_and_stream(pkg.name, root_password))[0] if uninstalled: if self.suggestions_cache: self.suggestions_cache.delete(pkg.name) return TransactionResult(success=True, installed=None, removed=[pkg]) return TransactionResult.fail()
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None) -> SearchResult: if snap.is_installed() and snapd.is_running(): snapd_client = SnapdClient(self.logger) app_names = {a['snap'] for a in snapd_client.list_only_apps()} installed = [ self._map_to_app(app_json=appjson, installed=True, disk_loader=disk_loader, is_application=app_names and appjson['name'] in app_names) for appjson in snapd_client.list_all_snaps() ] return SearchResult(installed, None, len(installed)) else: return SearchResult([], None, 0)
def install(self, pkg: SnapApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult: # retrieving all installed so it will be possible to know the additional installed runtimes after the operation succeeds if not snap.is_installed(): watcher.print("'snap' seems not to be installed") return TransactionResult.fail() if not snapd.is_running(): watcher.print("'snapd' seems not to be running") return TransactionResult.fail() installed_names = { s['name'] for s in SnapdClient(self.logger).list_all_snaps() } client = SnapdClient(self.logger) snap_config = read_config() try: channel = self._request_channel_installation( pkg=pkg, snap_config=snap_config, snapd_client=client, watcher=watcher) pkg.channel = channel except: watcher.print('Aborted by user') return TransactionResult.fail() res, output = ProcessHandler(watcher).handle_simple( snap.install_and_stream(app_name=pkg.name, confinement=pkg.confinement, root_password=root_password, channel=channel)) if 'error:' in output: res = False if 'not available on stable' in output: channels = RE_AVAILABLE_CHANNELS.findall(output) if channels: opts = [ InputOption(label=c[0], value=c[1]) for c in channels ] channel_select = SingleSelectComponent( type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0]) body = '<p>{}.</p>'.format( self.i18n['snap.install.available_channels.message']. format(bold(self.i18n['stable']), bold(pkg.name))) body += '<p>{}:</p>'.format( self.i18n['snap.install.available_channels.help']) if watcher.request_confirmation( title=self. i18n['snap.install.available_channels.title'], body=body, components=[channel_select], confirmation_label=self.i18n['continue'], deny_label=self.i18n['cancel']): self.logger.info( "Installing '{}' with the custom command '{}'". format(pkg.name, channel_select.value)) res = ProcessHandler(watcher).handle( SystemProcess( new_root_subprocess( channel_select.value.value.split(' '), root_password=root_password))) return self._gen_installation_response( success=res, pkg=pkg, installed=installed_names, disk_loader=disk_loader) else: self.logger.error( "Could not find available channels in the installation output: {}" .format(output)) return self._gen_installation_response(success=res, pkg=pkg, installed=installed_names, disk_loader=disk_loader)
def list_suggestions( self, limit: int, filter_installed: bool) -> Optional[List[PackageSuggestion]]: if limit == 0 or not snapd.is_running(): return if self.is_local_suggestions_file_mapped(): suggestions_str = self._read_local_suggestions_file() else: suggestions_str = self._download_remote_suggestions_file() if suggestions_str is None: return if not suggestions_str: self.logger.warning( f"No Snap suggestion found in {self.suggestions_url}") return ids_prios = suggestions.parse(suggestions_str, self.logger, 'Snap') if not ids_prios: self.logger.warning( f"No Snap suggestion could be parsed from {self.suggestions_url}" ) return suggestion_by_priority = suggestions.sort_by_priority(ids_prios) snapd_client = SnapdClient(self.logger) if filter_installed: installed = { s['name'].lower() for s in snapd_client.list_all_snaps() } if installed: suggestion_by_priority = tuple(n for n in suggestion_by_priority if n not in installed) if suggestion_by_priority and 0 < limit < len(suggestion_by_priority): suggestion_by_priority = suggestion_by_priority[0:limit] self.logger.info( f'Available Snap suggestions: {len(suggestion_by_priority)}') if not suggestion_by_priority: return self.logger.info("Mapping Snap suggestions") instances, threads = [], [] res, cached_count = [], 0 for name in suggestion_by_priority: cached_sug = self.suggestions_cache.get(name) if cached_sug: res.append(cached_sug) cached_count += 1 else: t = Thread(target=self._fill_suggestion, args=(name, ids_prios[name], snapd_client, res)) t.start() threads.append(t) time.sleep(0.001) # to avoid being blocked for t in threads: t.join() if cached_count > 0: self.logger.info( f"Returning {cached_count} cached Snap suggestions") return res