Example #1
0
    def upgrade(self, requirements: UpgradeRequirements, root_password: str, watcher: ProcessWatcher) -> bool:
        flatpak_version = flatpak.get_version()
        for req in requirements.to_upgrade:
            watcher.change_status("{} {} ({})...".format(self.i18n['manage_window.status.upgrading'], req.pkg.name, req.pkg.version))
            related, deps = False, False
            ref = req.pkg.ref

            if req.pkg.partial and flatpak_version < '1.5':
                related, deps = True, True
                ref = req.pkg.base_ref

            try:
                res = ProcessHandler(watcher).handle(SystemProcess(subproc=flatpak.update(app_ref=ref,
                                                                                          installation=req.pkg.installation,
                                                                                          related=related,
                                                                                          deps=deps)))

                watcher.change_substatus('')
                if not res:
                    self.logger.warning("Could not upgrade '{}'".format(req.pkg.id))
                    return False
            except:
                watcher.change_substatus('')
                self.logger.error("An error occurred while upgrading '{}'".format(req.pkg.id))
                traceback.print_exc()
                return False

        watcher.change_substatus('')
        return True
Example #2
0
    def search(self,
               words: str,
               disk_loader: DiskCacheLoader,
               limit: int = -1,
               is_url: bool = False) -> SearchResult:
        if is_url:
            return SearchResult([], [], 0)

        remote_level = self._get_search_remote()

        res = SearchResult([], [], 0)
        apps_found = flatpak.search(flatpak.get_version(), words, remote_level)

        if apps_found:
            already_read = set()
            installed_apps = self.read_installed(
                disk_loader=disk_loader, internet_available=True).installed

            if installed_apps:
                for app_found in apps_found:
                    for installed_app in installed_apps:
                        if app_found['id'] == installed_app.id:
                            res.installed.append(installed_app)
                            already_read.add(app_found['id'])

            if len(apps_found) > len(already_read):
                for app_found in apps_found:
                    if app_found['id'] not in already_read:
                        res.new.append(
                            self._map_to_model(app_found, False, disk_loader))

        res.total = len(res.installed) + len(res.new)
        return res
Example #3
0
    def get_upgrade_requirements(
            self, pkgs: List[FlatpakApplication], root_password: str,
            watcher: ProcessWatcher) -> UpgradeRequirements:
        flatpak_version = flatpak.get_version()

        user_pkgs, system_pkgs = [], []

        for pkg in pkgs:
            if pkg.installation == 'user':
                user_pkgs.append(pkg)
            else:
                system_pkgs.append(pkg)

        for apps_by_install in ((user_pkgs, 'user'), (system_pkgs, 'system')):
            if apps_by_install[0]:
                sizes = flatpak.map_update_download_size(
                    [str(p.id) for p in apps_by_install[0]],
                    apps_by_install[1], flatpak_version)

                if sizes:
                    for p in apps_by_install[0]:
                        p.size = sizes.get(str(p.id))

        to_update = [
            UpgradeRequirement(pkg=p, extra_size=p.size, required_size=p.size)
            for p in self.sort_update_order(pkgs)
        ]
        return UpgradeRequirements(None, None, to_update, [])
Example #4
0
    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:
        version = flatpak.get_version()

        updates = []

        if internet_available:
            thread_updates = Thread(target=self._add_updates, args=(version, updates))
            thread_updates.start()
        else:
            thread_updates = None

        installed = flatpak.list_installed(version)
        models = []

        if installed:
            if thread_updates:
                thread_updates.join()

            for app_json in installed:
                model = self._map_to_model(app_json=app_json, installed=True,
                                           disk_loader=disk_loader, internet=internet_available)
                if version >= '1.5.0':
                    model.update = '{}/{}'.format(app_json['id'], app_json['branch']) in updates[0] if updates else None
                else:
                    model.update = app_json['ref'] in updates[0] if updates else None

                models.append(model)

        return SearchResult(models, None, len(models))
Example #5
0
    def downgrade(self, pkg: FlatpakApplication, root_password: Optional[str], watcher: ProcessWatcher) -> bool:
        if not self._make_exports_dir(watcher):
            return False

        watcher.change_progress(10)
        watcher.change_substatus(self.i18n['flatpak.downgrade.commits'])

        history = self.get_history(pkg, full_commit_str=True)

        # downgrade is not possible if the app current commit in the first one:
        if history.pkg_status_idx == len(history.history) - 1:
            watcher.show_message(self.i18n['flatpak.downgrade.impossible.title'],
                                 self.i18n['flatpak.downgrade.impossible.body'].format(bold(pkg.name)),
                                 MessageType.ERROR)
            return False

        commit = history.history[history.pkg_status_idx + 1]['commit']
        watcher.change_substatus(self.i18n['flatpak.downgrade.reverting'])
        watcher.change_progress(50)
        success, _ = ProcessHandler(watcher).handle_simple(flatpak.downgrade(app_ref=pkg.ref,
                                                                             commit=commit,
                                                                             installation=pkg.installation,
                                                                             root_password=root_password,
                                                                             version=flatpak.get_version()))
        watcher.change_progress(100)
        return success
Example #6
0
    def search(self,
               words: str,
               disk_loader: DiskCacheLoader,
               limit: int = -1) -> SearchResult:

        res = SearchResult([], [], 0)
        apps_found = flatpak.search(flatpak.get_version(), words)

        if apps_found:
            already_read = set()
            installed_apps = self.read_installed(
                disk_loader=disk_loader).installed

            if installed_apps:
                for app_found in apps_found:
                    for installed_app in installed_apps:
                        if app_found['id'] == installed_app.id:
                            res.installed.append(installed_app)
                            already_read.add(app_found['id'])

            if len(apps_found) > len(already_read):
                for app_found in apps_found:
                    if app_found['id'] not in already_read:
                        res.new.append(
                            self._map_to_model(app_found, False, disk_loader))

        res.total = len(res.installed) + len(res.new)
        return res
Example #7
0
    def get_info(self, app: FlatpakApplication) -> dict:
        if app.installed:
            if app.update_component:
                app_info = {'id': app.id,
                            'name': app.name,
                            'branch': app.branch,
                            'installation': app.installation,
                            'origin': app.origin,
                            'arch': app.arch,
                            'ref': app.ref,
                            'type': 'runtime' if app.runtime else self.i18n['unknown']}
            else:
                version = flatpak.get_version()
                id_ = app.base_id if app.partial and version < VERSION_1_5 else app.id
                app_info = flatpak.get_app_info_fields(id_, app.branch, app.installation)

                if app.partial and version < VERSION_1_5:
                    app_info['id'] = app.id
                    app_info['ref'] = app.ref

                app_info['name'] = app.name
                app_info['type'] = 'runtime' if app.runtime else 'app'
                app_info['description'] = strip_html(app.description) if app.description else ''

                if app.installation:
                    app_info['installation'] = app.installation

                if app_info.get('installed'):
                    app_info['installed'] = app_info['installed'].replace('?', ' ')

            return app_info
        else:
            res = self.http_client.get_json('{}/apps/{}'.format(FLATHUB_API_URL, app.id))

            if res:
                if res.get('categories'):
                    res['categories'] = [c.get('name') for c in res['categories']]

                for to_del in ('screenshots', 'iconMobileUrl', 'iconDesktopUrl'):
                    if res.get(to_del):
                        del res[to_del]

                for to_strip in ('description', 'currentReleaseDescription'):
                    if res.get(to_strip):
                        res[to_strip] = strip_html(res[to_strip])

                for to_date in ('currentReleaseDate', 'inStoreSinceDate'):
                    if res.get(to_date):
                        try:
                            res[to_date] = datetime.strptime(res[to_date], DATE_FORMAT)
                        except:
                            self.context.logger.error('Could not convert date string {} as {}'.format(res[to_date], DATE_FORMAT))
                            pass

                return res
            else:
                return {}
Example #8
0
    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:
        version = flatpak.get_version()

        updates = []

        if internet_available:
            thread_updates = Thread(target=self._add_updates, args=(version, updates))
            thread_updates.start()
        else:
            thread_updates = None

        installed = flatpak.list_installed(version)
        models = []

        if installed:
            update_map = None
            if thread_updates:
                thread_updates.join()
                update_map = updates[0]

            for app_json in installed:
                model = self._map_to_model(app_json=app_json, installed=True,
                                           disk_loader=disk_loader, internet=internet_available)
                model.update = None
                models.append(model)

                if update_map and (update_map['full'] or update_map['partial']):
                    if version >= '1.4.0':
                        update_id = '{}/{}/{}'.format(app_json['id'], app_json['branch'], app_json['installation'])

                        if update_map['full'] and update_id in update_map['full']:
                            model.update = True

                        if update_map['partial']:
                            for partial in update_map['partial']:
                                partial_data = partial.split('/')
                                if app_json['id'] in partial_data[0] and\
                                        app_json['branch'] == partial_data[1] and\
                                        app_json['installation'] == partial_data[2]:
                                    partial_model = model.gen_partial(partial.split('/')[0])
                                    partial_model.update = True
                                    models.append(partial_model)
                    else:
                        model.update = '{}/{}'.format(app_json['installation'], app_json['ref']) in update_map['full']

        if models:
            ignored = self._read_ignored_updates()

            if ignored:
                for model in models:
                    if model.get_update_ignore_key() in ignored:
                        model.updates_ignored = True

        return SearchResult(models, None, len(models))
Example #9
0
    def list_suggestions(self, limit: int,
                         filter_installed: bool) -> List[PackageSuggestion]:
        cli_version = flatpak.get_version()
        res = []

        self.logger.info(
            "Downloading the 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")
            remote_level = self._get_search_remote()
            installed = {
                i.id
                for i in self.read_installed(disk_loader=None).installed
            } if filter_installed else None

            for line in file.text.split('\n'):
                if line:
                    if limit <= 0 or len(res) < limit:
                        sug = line.split('=')
                        appid = sug[1].strip()

                        if installed and appid in installed:
                            continue

                        priority = SuggestionPriority(int(sug[0]))

                        cached_sug = self.suggestions_cache.get(appid)

                        if cached_sug:
                            res.append(cached_sug)
                        else:
                            app_json = flatpak.search(cli_version,
                                                      appid,
                                                      remote_level,
                                                      app_id=True)

                            if app_json:
                                model = PackageSuggestion(
                                    self._map_to_model(app_json[0], False,
                                                       None), priority)
                                self.suggestions_cache.add(appid, model)
                                res.append(model)
                    else:
                        break

            res.sort(key=lambda s: s.priority.value, reverse=True)
        return res
Example #10
0
    def update(self, pkg: FlatpakApplication, root_password: str,
               watcher: ProcessWatcher) -> bool:
        related, deps = False, False
        ref = pkg.ref

        if pkg.partial and flatpak.get_version() < '1.5':
            related, deps = True, True
            ref = pkg.base_ref

        return ProcessHandler(watcher).handle(
            SystemProcess(subproc=flatpak.update(app_ref=ref,
                                                 installation=pkg.installation,
                                                 related=related,
                                                 deps=deps)))
Example #11
0
    def uninstall(self, pkg: FlatpakApplication, root_password: Optional[str], watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:

        if not self._make_exports_dir(watcher):
            return TransactionResult.fail()

        flatpak_version = flatpak.get_version()
        uninstalled, _ = ProcessHandler(watcher).handle_simple(flatpak.uninstall(pkg.ref, pkg.installation,
                                                                                 flatpak_version))

        if uninstalled:
            if self.suggestions_cache:
                self.suggestions_cache.delete(pkg.id)

            self.revert_ignored_update(pkg)
            return TransactionResult(success=True, installed=None, removed=[pkg])

        return TransactionResult.fail()
Example #12
0
    def upgrade(self, requirements: UpgradeRequirements, root_password: Optional[str], watcher: ProcessWatcher) -> bool:
        flatpak_version = flatpak.get_version()

        if not self._make_exports_dir(watcher):
            return False

        for req in requirements.to_upgrade:
            watcher.change_status("{} {} ({})...".format(self.i18n['manage_window.status.upgrading'], req.pkg.name, req.pkg.version))
            related, deps = False, False
            ref = req.pkg.ref

            if req.pkg.partial and flatpak_version < VERSION_1_5:
                related, deps = True, True
                ref = req.pkg.base_ref

            try:
                if req.pkg.update_component:
                    self.logger.info(f"Installing {req.pkg}")
                    res, _ = ProcessHandler(watcher).handle_simple(flatpak.install(app_id=ref,
                                                                                   installation=req.pkg.installation,
                                                                                   origin=req.pkg.origin,
                                                                                   version=flatpak_version))

                else:
                    self.logger.info(f"Updating {req.pkg}")
                    res, _ = ProcessHandler(watcher).handle_simple(flatpak.update(app_ref=ref,
                                                                                  installation=req.pkg.installation,
                                                                                  related=related,
                                                                                  deps=deps,
                                                                                  version=flatpak_version))

                watcher.change_substatus('')
                if not res:
                    self.logger.warning("Could not upgrade '{}'".format(req.pkg.id))
                    return False
            except:
                watcher.change_substatus('')
                self.logger.error("An error occurred while upgrading '{}'".format(req.pkg.id))
                traceback.print_exc()
                return False

        watcher.change_substatus('')
        return True
Example #13
0
    def list_suggestions(self, limit: int) -> List[PackageSuggestion]:
        cli_version = flatpak.get_version()
        res = []

        sugs = [(i, p) for i, p in suggestions.ALL.items()]
        sugs.sort(key=lambda t: t[1].value, reverse=True)

        for sug in sugs:

            if limit <= 0 or len(res) < limit:
                app_json = flatpak.search(cli_version, sug[0], app_id=True)

                if app_json:
                    res.append(
                        PackageSuggestion(
                            self._map_to_model(app_json[0], False, None),
                            sug[1]))
            else:
                break

        res.sort(key=lambda s: s.priority.value, reverse=True)
        return res
Example #14
0
    def read_installed(self, disk_loader: Optional[DiskCacheLoader], limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None,
                       internet_available: bool = None, wait_async_data: bool = False) -> SearchResult:
        version = flatpak.get_version()

        updates, required_runtimes = list(), dict()

        thread_updates, thread_runtimes = None, None
        if internet_available:
            thread_updates = Thread(target=self._add_updates, args=(version, updates))
            thread_updates.start()

            if version >= VERSION_1_12:
                thread_runtimes = Thread(target=self._fill_required_runtime_updates, args=(required_runtimes,))
                thread_runtimes.start()

        installed = flatpak.list_installed(version)

        update_map = None
        if thread_updates:
            thread_updates.join()
            update_map = updates[0]

        models = {}
        data_loaders: Optional[List[FlatpakAsyncDataLoader]] = [] if wait_async_data else None

        if installed:
            for app_json in installed:
                model, loader = self._map_to_model(app_json=app_json, installed=True,
                                                   disk_loader=disk_loader, internet=internet_available)
                model.update = False
                models[model.get_update_id(version)] = model

                if loader and data_loaders is not None:
                    data_loaders.append(loader)

        if update_map:
            for update_id in update_map['full']:
                model_with_update = models.get(update_id)
                if model_with_update:
                    model_with_update.update = True
                else:
                    # it is a new component that must be installed
                    update_id_split = update_id.split('/')
                    new_app = FlatpakApplication(id=update_id_split[0],
                                                 branch=update_id_split[1],
                                                 installation=update_id_split[2],
                                                 name=update_id_split[0].split('.')[-1].strip(),
                                                 version=update_id_split[1],
                                                 arch='x86_64' if self.context.is_system_x86_64() else 'x86',
                                                 origin=update_id_split[3] if len(update_id_split) == 4 else None)
                    new_app.update_component = True  # mark as "update component"
                    new_app.installed = True  # faking the "installed" status to be displayed as an update
                    new_app.update = True
                    new_app.update_ref()
                    models[update_id] = new_app

            if version >= VERSION_1_2:
                for partial_update_id in update_map['partial']:
                    partial_data = partial_update_id.split('/')

                    for model in models.values():
                        if model.installation == partial_data[2] and model.branch == partial_data[1]:
                            if model.id == partial_data[0]:
                                model.update = True
                                break
                            elif model.id in partial_data[0]:
                                partial_model = model.gen_partial(partial_data[0])
                                partial_model.update = True
                                models[partial_update_id] = partial_model
                                break

        if thread_runtimes:
            thread_runtimes.join()

        if required_runtimes:
            for installation in ('system', 'user'):
                installation_runtimes = required_runtimes.get(installation)

                if installation_runtimes:
                    for ref, origin in installation_runtimes:
                        ref_split = ref.split('/')
                        models[f'{installation}.'] = FlatpakApplication(id=ref_split[1],
                                                                        ref=ref,
                                                                        origin=origin,
                                                                        name=ref_split[1],
                                                                        version=ref_split[-1],
                                                                        latest_version=ref_split[-1],
                                                                        runtime=True,
                                                                        installation=installation,
                                                                        installed=False,
                                                                        update_component=True,
                                                                        update=True)

        if models:
            ignored = self._read_ignored_updates()

            if ignored:
                for model in models.values():
                    if model.get_update_ignore_key() in ignored:
                        model.updates_ignored = True

        if data_loaders:
            for loader in data_loaders:
                loader.join()

        return SearchResult([*models.values()], None, len(models))
Example #15
0
    def install(self, pkg: FlatpakApplication, root_password: str,
                disk_loader: DiskCacheLoader,
                watcher: ProcessWatcher) -> TransactionResult:
        flatpak_config = self.configman.get_config()

        install_level = flatpak_config['installation_level']

        if install_level is not None:
            self.logger.info(
                "Default Flaptak installation level defined: {}".format(
                    install_level))

            if install_level not in ('user', 'system'):
                watcher.show_message(
                    title=self.i18n['error'].capitalize(),
                    body=self.i18n['flatpak.install.bad_install_level.body'].
                    format(field=bold('installation_level'),
                           file=bold(CONFIG_FILE)),
                    type_=MessageType.ERROR)
                return TransactionResult(success=False,
                                         installed=[],
                                         removed=[])

            pkg.installation = install_level
        else:
            user_level = watcher.request_confirmation(
                title=self.i18n['flatpak.install.install_level.title'],
                body=self.i18n['flatpak.install.install_level.body'].format(
                    bold(pkg.name)),
                confirmation_label=self.i18n['no'].capitalize(),
                deny_label=self.i18n['yes'].capitalize())
            pkg.installation = 'user' if user_level else 'system'

        remotes = flatpak.list_remotes()

        handler = ProcessHandler(watcher)

        if pkg.installation == 'user' and not remotes['user']:
            handler.handle_simple(flatpak.set_default_remotes('user'))
        elif pkg.installation == 'system' and not remotes['system']:
            if user.is_root():
                handler.handle_simple(flatpak.set_default_remotes('system'))
            else:
                valid, user_password = watcher.request_root_password()
                if not valid:
                    watcher.print('Operation aborted')
                    return TransactionResult(success=False,
                                             installed=[],
                                             removed=[])
                else:
                    if not handler.handle_simple(
                            flatpak.set_default_remotes(
                                'system', user_password))[0]:
                        watcher.show_message(
                            title=self.i18n['error'].capitalize(),
                            body=self.
                            i18n['flatpak.remotes.system_flathub.error'],
                            type_=MessageType.ERROR)
                        watcher.print("Operation cancelled")
                        return TransactionResult(success=False,
                                                 installed=[],
                                                 removed=[])

        # retrieving all installed so it will be possible to know the additional installed runtimes after the operation succeeds
        flatpak_version = flatpak.get_version()
        installed = flatpak.list_installed(flatpak_version)
        installed_by_level = {
            '{}:{}:{}'.format(p['id'], p['name'], p['branch'])
            for p in installed if p['installation'] == pkg.installation
        } if installed else None

        if not self._make_exports_dir(handler.watcher):
            return TransactionResult(success=False, installed=[], removed=[])

        installed, output = handler.handle_simple(
            flatpak.install(str(pkg.id), pkg.origin, pkg.installation))

        if not installed and 'error: No ref chosen to resolve matches' in output:
            ref_opts = RE_INSTALL_REFS.findall(output)

            if ref_opts and len(ref_opts) > 1:
                view_opts = [
                    InputOption(label=o, value=o.strip()) for o in ref_opts
                    if o
                ]
                ref_select = SingleSelectComponent(type_=SelectViewType.RADIO,
                                                   options=view_opts,
                                                   default_option=view_opts[0],
                                                   label='')
                if watcher.request_confirmation(
                        title=self.i18n['flatpak.install.ref_choose.title'],
                        body=self.i18n['flatpak.install.ref_choose.body'].
                        format(bold(pkg.name)),
                        components=[ref_select],
                        confirmation_label=self.i18n['proceed'].capitalize(),
                        deny_label=self.i18n['cancel'].capitalize()):
                    ref = ref_select.get_selected()
                    installed, output = handler.handle_simple(
                        flatpak.install(ref, pkg.origin, pkg.installation))
                    pkg.ref = ref
                    pkg.runtime = 'runtime' in ref
                else:
                    watcher.print('Aborted by the user')
                    return TransactionResult.fail()
            else:
                return TransactionResult.fail()

        if installed:
            try:
                fields = flatpak.get_fields(str(pkg.id), pkg.branch,
                                            ['Ref', 'Branch'])

                if fields:
                    pkg.ref = fields[0]
                    pkg.branch = fields[1]
            except:
                traceback.print_exc()

        if installed:
            new_installed = [pkg]
            current_installed = flatpak.list_installed(flatpak_version)
            current_installed_by_level = [
                p for p in current_installed
                if p['installation'] == pkg.installation
            ] if current_installed else None

            if current_installed_by_level and (not installed_by_level or
                                               len(current_installed_by_level)
                                               > len(installed_by_level) + 1):
                pkg_key = '{}:{}:{}'.format(pkg.id, pkg.name, pkg.branch)
                net_available = self.context.is_internet_available()
                for p in current_installed_by_level:
                    current_key = '{}:{}:{}'.format(p['id'], p['name'],
                                                    p['branch'])
                    if current_key != pkg_key and (not installed_by_level
                                                   or current_key
                                                   not in installed_by_level):
                        new_installed.append(
                            self._map_to_model(app_json=p,
                                               installed=True,
                                               disk_loader=disk_loader,
                                               internet=net_available))

            return TransactionResult(success=installed,
                                     installed=new_installed,
                                     removed=[])
        else:
            return TransactionResult.fail()
Example #16
0
 def full_update(self, root_password: Optional[str], watcher: ProcessWatcher) -> bool:
     handler = ProcessHandler(watcher)
     return handler.handle_simple(flatpak.full_update(flatpak.get_version()))[0]
Example #17
0
    def list_suggestions(self, limit: int, filter_installed: bool) -> Optional[List[PackageSuggestion]]:
        if limit == 0:
            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 Flatpak suggestion found in {self.suggestions_file_url}")
            return

        ids_prios = suggestions.parse(suggestions_str, self.logger, 'Flatpak')

        if not ids_prios:
            self.logger.warning(f"No Flatpak suggestion could be parsed from {self.suggestions_file_url}")
            return

        suggestion_by_priority = suggestions.sort_by_priority(ids_prios)

        if filter_installed:
            installed = {i.id for i in self.read_installed(disk_loader=None).installed}

            if installed:
                suggestion_by_priority = tuple(id_ for id_ in suggestion_by_priority if id_ 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 Flatpak suggestions: {len(suggestion_by_priority)}')

        if not suggestion_by_priority:
            return

        flatpak_version = flatpak.get_version()
        remote = self._get_search_remote()

        self.logger.info("Mapping Flatpak suggestions")
        res, fill_suggestions = [], []
        cached_count = 0

        for appid in suggestion_by_priority:
            cached_instance = self.suggestions_cache.get(appid)

            if cached_instance:
                res.append(cached_instance)
                cached_count += 1
            else:
                fill = Thread(target=self._fill_suggestion, args=(appid, ids_prios[appid], flatpak_version,
                                                                  remote, res))
                fill.start()
                fill_suggestions.append(fill)

        for fill in fill_suggestions:
            fill.join()

        if cached_count > 0:
            self.logger.info(f"Returning {cached_count} cached Flatpak suggestions")

        return res