def _handle_conflict_to_update_and_to_install( self, pkg1: str, pkg2: str, pkg1_to_install: bool, context: UpdateRequirementsContext): to_install, to_update = (pkg1, pkg2) if pkg1_to_install else (pkg2, pkg1) to_install_srcs = { p for p, data in context.pkgs_data.items() if data['d'] and to_install in data['d'] } if to_update not in context.cannot_upgrade: srcs_str = ', '.join(("'{}'".format(p) for p in to_install_srcs)) reason = self.i18n[ 'arch.update_summary.to_update.conflicts_dep'].format( "'{}'".format(to_install), srcs_str) context.cannot_upgrade[to_install] = UpgradeRequirement( context.to_update[to_update], reason) if to_update in context.to_update: del context.to_update[to_update] for src_pkg in to_install_srcs: src_to_install = src_pkg in context.to_install pkg = context.to_install[ src_pkg] if src_to_install else context.to_update[src_pkg] if src_pkg not in context.cannot_upgrade: reason = self.i18n[ 'arch.update_summary.to_update.dep_conflicts'].format( "'{}'".format(to_install), "'{}'".format(to_update)) context.cannot_upgrade[src_pkg] = UpgradeRequirement( pkg, reason) if src_to_install: del context.to_install[src_pkg] if src_pkg in context.repo_to_install: del context.repo_to_install[src_pkg] else: del context.aur_to_install[src_pkg] else: del context.to_update[src_pkg] if src_pkg in context.repo_to_update: del context.repo_to_update[src_pkg] else: del context.aur_to_update[src_pkg] del context.pkgs_data[src_pkg] if to_install in context.to_install: del context.to_install[to_install]
def _fill_conflicts(self, context: UpdateRequirementsContext, blacklist: Iterable[str] = None): self.logger.info("Checking conflicts") root_conflict = self._filter_and_map_conflicts(context) sub_conflict = pacman.get_dependencies_to_remove( root_conflict.keys(), context.root_password) if root_conflict else None to_remove_map = {} if sub_conflict: for dep, source in sub_conflict.items(): if dep not in to_remove_map and (not blacklist or dep not in blacklist): req = ArchPackage(name=dep, installed=True, i18n=self.i18n) to_remove_map[dep] = req reason = "{} '{}'".format( self.i18n['arch.info.depends on'].capitalize(), source) context.to_remove[dep] = UpgradeRequirement(req, reason) if root_conflict: for dep, source in root_conflict.items(): if dep not in to_remove_map and (not blacklist or dep not in blacklist): req = ArchPackage(name=dep, installed=True, i18n=self.i18n) to_remove_map[dep] = req reason = "{} '{}'".format( self.i18n['arch.info.conflicts with'].capitalize(), source) context.to_remove[dep] = UpgradeRequirement(req, reason) if to_remove_map: for name in to_remove_map.keys(): # upgrading lists if name in context.pkgs_data: del context.pkgs_data[name] if name in context.aur_to_update: del context.aur_to_update[name] if name in context.repo_to_update: del context.repo_to_update[name] removed_size = pacman.get_installed_size([*to_remove_map.keys()]) if removed_size: for name, size in removed_size.items(): if size is not None: req = context.to_remove.get(name) if req: req.extra_size = size
def _map_requirement(self, pkg: ArchPackage, context: UpdateRequirementsContext, installed_sizes: Dict[str, int] = None) -> UpgradeRequirement: requirement = UpgradeRequirement(pkg) if pkg.repository != 'aur': data = context.pkgs_data[pkg.name] requirement.required_size = data['ds'] requirement.extra_size = data['s'] current_size = installed_sizes.get(pkg.name) if installed_sizes else None if current_size is not None and data['s']: requirement.extra_size = data['s'] - current_size return requirement
def get_upgrade_requirements(self, pkgs: List[AppImage], root_password: Optional[str], watcher: ProcessWatcher) -> UpgradeRequirements: to_update = [] for pkg in pkgs: requirement = UpgradeRequirement(pkg) installed_size = self.http_client.get_content_length_in_bytes(pkg.url_download) upgrade_size = self.http_client.get_content_length_in_bytes(pkg.url_download_latest_version) requirement.required_size = upgrade_size if upgrade_size and installed_size: requirement.extra_size = upgrade_size - installed_size to_update.append(requirement) return UpgradeRequirements([], [], to_update, [])
def _handle_conflict_both_to_install(self, pkg1: str, pkg2: str, context: UpdateRequirementsContext): for src_pkg in {p for p, data in context.pkgs_data.items() if data['d'] and pkg1 in data['d'] or pkg2 in data['d']}: if src_pkg not in context.cannot_upgrade: reason = self.i18n['arch.update_summary.to_install.dep_conflict'].format("'{}'".format(pkg1), "'{}'".format(pkg2)) context.cannot_upgrade[src_pkg] = UpgradeRequirement(context.to_update[src_pkg], reason) del context.to_update[src_pkg] if src_pkg in context.repo_to_update: del context.repo_to_update[src_pkg] else: del context.aur_to_update[src_pkg] del context.pkgs_data[src_pkg] for p in (pkg1, pkg2): if p in context.to_install: del context.to_install[p] if p in context.repo_to_install: del context.repo_to_install[p] else: del context.aur_to_install[p]
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, [])
def update_file(self, pkg: AppImage, root_password: Optional[str], watcher: ProcessWatcher): max_width = 350 file_chooser = FileChooserComponent(label=self.i18n['file'].capitalize(), allowed_extensions={'AppImage', '*'}, search_path=get_default_manual_installation_file_dir(), max_width=max_width) input_version = TextInputComponent(label=self.i18n['version'].capitalize(), max_width=max_width) file_chooser.observers.append(ManualInstallationFileObserver(None, input_version)) while True: if watcher.request_confirmation(title=self.i18n['appimage.custom_action.manual_update.details'], body=None, components=[FormComponent(label='', components=[file_chooser, input_version], spaces=False)], confirmation_label=self.i18n['proceed'].capitalize(), deny_label=self.i18n['cancel'].capitalize(), min_height=100, max_width=max_width + 150): if not file_chooser.file_path or not os.path.isfile(file_chooser.file_path) or not file_chooser.file_path.lower().strip().endswith('.appimage'): watcher.request_confirmation(title=self.i18n['error'].capitalize(), body=self.i18n['appimage.custom_action.install_file.invalid_file'], deny_button=False) else: break else: return False pkg.local_file_path = file_chooser.file_path pkg.version = input_version.get_value() reqs = UpgradeRequirements(to_install=None, to_remove=None, to_upgrade=[UpgradeRequirement(pkg=pkg)], cannot_upgrade=None) return self.upgrade(reqs, root_password=root_password, watcher=watcher)
def _handle_conflict_both_to_update(self, pkg1: str, pkg2: str, context: UpdateRequirementsContext): if pkg1 not in context.cannot_upgrade: reason = "{} '{}'".format(self.i18n['arch.info.conflicts with'].capitalize(), pkg2) context.cannot_upgrade[pkg1] = UpgradeRequirement(pkg=context.to_update[pkg1], reason=reason) if pkg2 not in context.cannot_upgrade: reason = "{} '{}'".format(self.i18n['arch.info.conflicts with'].capitalize(), pkg1) context.cannot_upgrade[pkg2] = UpgradeRequirement(pkg=context.to_update[pkg2], reason=reason) for p in (pkg1, pkg2): if p in context.to_update: del context.to_update[p] if p in context.repo_to_update: del context.repo_to_update[p] else: del context.aur_to_update[p]
def _add_to_remove(self, pkgs_to_sync: Set[str], names: Dict[str, Set[str]], context: UpdateRequirementsContext, to_ignore: Set[str] = None): blacklist = to_ignore if to_ignore else set() blacklist.update(names) dependents = {} for pname in pkgs_to_sync: if pname not in blacklist: data = context.pkgs_data.get(pname) if data: deps = data.get('d') if deps: for n in names: if n in deps: all_deps = dependents.get(n, set()) all_deps.update(pname) dependents[n] = all_deps else: self.logger.warning( "Package '{}' to sync could not be removed from the transaction context because its data was not loaded" ) for n in names: if n in context.pkgs_data: if n not in context.to_remove: depends_on = names.get(n) if depends_on: reason = "{} '{}'".format( self.i18n['arch.info.depends on'].capitalize(), ', '.join(depends_on)) else: reason = '?' context.to_remove[n] = UpgradeRequirement(pkg=ArchPackage( name=n, installed=True, i18n=self.i18n), reason=reason) all_deps = dependents.get(n) if all_deps: self._add_to_remove(pkgs_to_sync, {dep: {n} for dep in all_deps}, context, blacklist) else: self.logger.warning( "Package '{}' could not be removed from the transaction context because its data was not loaded" )
def _add_dependents_as_cannot_upgrade( self, context: UpdateRequirementsContext, names: Iterable[str], pkgs_available: Set[ArchPackage], already_removed: Optional[Set[str]] = None, iteration_level: int = 0) -> Set[str]: removed = set() if already_removed is None else already_removed removed.update(names) available = {p for p in pkgs_available if p.name not in removed} to_remove = set() if available: for pkg in available: if pkg.name not in removed: data = context.pkgs_data.get(pkg.name) if data and data['d']: for dep in data['d']: dep_providers = context.provided_map.get(dep) if dep_providers: for p in dep_providers: if p in names: to_remove.add(pkg.name) if pkg.name not in context.cannot_upgrade: reason = "{} {}".format( self. i18n['arch.info.depends on']. capitalize(), p) context.cannot_upgrade[ pkg.name] = UpgradeRequirement( pkg=pkg, reason=reason, sorting_priority= iteration_level - 1) break if to_remove: removed.update(to_remove) self._add_dependents_as_cannot_upgrade( context=context, names=to_remove, pkgs_available=available, already_removed=to_remove, iteration_level=iteration_level - 1) return to_remove
def _add_dependency_breakage(self, pkgname: str, pkgdeps: Optional[Set[str]], provided_versions: Dict[str, Set[str]], cannot_upgrade: Set[str], context: UpdateRequirementsContext): if pkgdeps: for dep in pkgdeps: dep_split = RE_DEP_OPERATORS.split(dep) if len(dep_split) > 1 and dep_split[1]: real_providers = context.provided_map.get(dep_split[0]) if real_providers: versions = provided_versions.get(dep_split[0]) if versions: op = ''.join(RE_DEP_OPERATORS.findall(dep)) if op == '=': op = '==' version_match = False for v in versions: try: provided_version, required_version = parse_version( v), parse_version(dep_split[1]) if eval('provided_version {} required_version' .format(op)): version_match = True break except: self.logger.error( "Error when comparing versions {} (provided) and {} (required)" .format(v, dep_split[1])) traceback.print_exc() if not version_match: for pname in real_providers: if pname not in cannot_upgrade: provider = context.to_update.get(pname) if provider: cannot_upgrade.add(pname) reason = self.i18n[ 'arch.sync.dep_breakage.reason'].format( pkgname, dep) context.cannot_upgrade[ pname] = UpgradeRequirement( pkg=provider, reason=reason)
def _fill_conflicts(self, context: UpdateRequirementsContext, blacklist: Iterable[str] = None): self.logger.info("Checking conflicts") root_conflict = self._filter_and_map_conflicts(context) if root_conflict: for dep, source in root_conflict.items(): if dep not in context.to_remove and (not blacklist or dep not in blacklist): req = ArchPackage(name=dep, installed=True, i18n=self.i18n) reason = "{} '{}'".format( self.i18n['arch.info.conflicts with'].capitalize(), source) context.to_remove[dep] = UpgradeRequirement(req, reason)
def _map_requirement(self, pkg: ArchPackage, context: UpdateRequirementsContext, installed_sizes: Dict[str, int] = None, to_install: bool = False, to_sync: Set[str] = None) -> UpgradeRequirement: requirement = UpgradeRequirement(pkg) if pkg.repository != 'aur': pkgdata = context.pkgs_data.get(pkg.name) if pkgdata: requirement.required_size = pkgdata['ds'] requirement.extra_size = pkgdata['s'] current_size = installed_sizes.get( pkg.name) if installed_sizes else None if current_size is not None and pkgdata['s'] is not None: requirement.extra_size = pkgdata['s'] - current_size required_by = set() if to_install and to_sync and context.pkgs_data: names = pkgdata.get('p', {pkg.name}) if pkgdata else {pkg.name} to_sync_deps_cache = {} for p in to_sync: if p != pkg.name and p in context.pkgs_data: deps = to_sync_deps_cache.get(p) if deps is None: deps = context.pkgs_data[p]['d'] if deps is None: deps = set() else: deps = { RE_DEP_OPERATORS.split(d)[0] for d in deps } to_sync_deps_cache[p] = deps if deps: for n in names: if n in deps: required_by.add(p) break requirement.reason = '{}: {}'.format( self.i18n['arch.info.required by'].capitalize(), ','.join(required_by) if required_by else '?') return requirement
def update_file(self, pkg: AppImage, root_password: str, watcher: ProcessWatcher): file_chooser = FileChooserComponent( label=self.i18n['file'].capitalize(), allowed_extensions={'AppImage'}) input_version = TextInputComponent( label=self.i18n['version'].capitalize()) while True: if watcher.request_confirmation( title=self. i18n['appimage.custom_action.manual_update.details'], body=None, components=[ FormComponent(label='', components=[file_chooser, input_version], spaces=False) ], confirmation_label=self.i18n['proceed'].capitalize(), deny_label=self.i18n['cancel'].capitalize()): if not file_chooser.file_path or not os.path.isfile( file_chooser.file_path): watcher.request_confirmation( title=self.i18n['error'].capitalize(), body=self.i18n[ 'appimage.custom_action.install_file.invalid_file'], deny_button=False) else: break else: return False pkg.local_file_path = file_chooser.file_path pkg.version = input_version.get_value() reqs = UpgradeRequirements(to_install=None, to_remove=None, to_upgrade=[UpgradeRequirement(pkg=pkg)], cannot_upgrade=None) return self.upgrade(reqs, root_password=root_password, watcher=watcher)
def __update_context_based_on_to_remove( self, context: UpdateRequirementsContext): if context.to_remove: to_remove_provided = {} # filtering all package to synchronization from the transaction context to_sync = { *(context.to_update.keys() if context.to_update else set()), *(context.to_install.keys() if context.to_install else set()) } if to_sync: # checking if any packages to sync on the context rely on the 'to remove' ones to_remove_provided.update( pacman.map_provided(remote=False, pkgs=context.to_remove.keys())) to_remove_from_sync = { } # will store all packages that should be removed for pname in to_sync: if pname in context.pkgs_data: deps = context.pkgs_data[pname].get('d') if deps: required = set() for pkg in context.to_remove: for provided in to_remove_provided[pkg]: if provided in deps: required.add(pkg) break if required: to_remove_from_sync[pname] = required else: self.logger.warning( "Conflict resolution: package '{}' marked to synchronization has no data loaded" ) if to_remove_from_sync: # removing all these packages and their dependents from the context self._add_to_remove(to_sync, to_remove_from_sync, context) # checking if the installed packages that are not in the transaction context rely on the current packages to be removed: current_to_remove = {*context.to_remove.keys()} required_by_installed = self.deps_analyser.map_all_required_by( current_to_remove, {*to_sync}) if required_by_installed: # updating provided context: provided_not_mapped = set() for pkg in current_to_remove.difference( {*to_remove_provided.keys()}): if pkg not in context.pkgs_data: provided_not_mapped.add(pkg) else: provided = context.pkgs_data[pkg].get('p') if provided: to_remove_provided[pkg] = provided else: provided_not_mapped.add(pkg) if provided_not_mapped: to_remove_provided.update( pacman.map_provided(remote=False, pkgs=provided_not_mapped)) deps_no_data = { dep for dep in required_by_installed if dep in context.pkgs_data } deps_nodata_deps = pacman.map_required_dependencies( *deps_no_data) if deps_no_data else {} reverse_to_remove_provided = { p: name for name, provided in to_remove_provided.items() for p in provided } for pkg in required_by_installed: if pkg not in context.to_remove: if pkg in context.pkgs_data: dep_deps = context.pkgs_data[pkg].get('d') else: dep_deps = deps_nodata_deps.get(pkg) if dep_deps: source = ', '.join( (reverse_to_remove_provided[d] for d in dep_deps if d in reverse_to_remove_provided)) reason = "{} '{}'".format( self.i18n['arch.info.depends on'].capitalize(), source if source else '?') context.to_remove[pkg] = UpgradeRequirement( pkg=ArchPackage(name=pkg, installed=True, i18n=self.i18n), reason=reason) for name in context.to_remove: # upgrading lists if name in context.pkgs_data: del context.pkgs_data[name] if name in context.aur_to_update: del context.aur_to_update[name] if name in context.repo_to_update: del context.repo_to_update[name] removed_size = pacman.get_installed_size( [*context.to_remove.keys()]) if removed_size: for name, size in removed_size.items(): if size is not None: req = context.to_remove.get(name) if req: req.extra_size = size
def get_upgrade_requirements(self, pkgs: List[DebianPackage], root_password: str, watcher: ProcessWatcher) \ -> UpgradeRequirements: transaction = self.aptitude.simulate_upgrade((p.name for p in pkgs)) if transaction: size_to_query = (f'{p.name}={p.latest_version}' for p in (*transaction.to_upgrade, *transaction.to_install, *transaction.to_remove)) update_extra_attrs = ('compressed size', *(('depends', 'predepends') if transaction.to_install else ())) update_data = self.aptitude.show(pkgs=size_to_query, attrs=update_extra_attrs) to_install = None if transaction.to_install: dependents = self._map_dependents(update_data) to_install = [] for p in transaction.to_install: p_data = update_data.get(p.name) if update_data else None req_size = p_data.get('compressed size') if p_data else None reason = None if dependents: p_dependents = dependents.get(p.name) if p_dependents: deps_str = ', '.join((bold(d) for d in p_dependents)) reason = self._i18n['debian.transaction.dependency_of'].format(pkgs=deps_str) to_install.append(UpgradeRequirement(pkg=p, reason=reason, required_size=req_size, extra_size=p.transaction_size)) to_install.sort(key=attrgetter('pkg.name')) to_install = tuple(to_install) to_remove = None if transaction.to_remove: to_remove = [] for p in transaction.to_remove: to_remove.append(UpgradeRequirement(pkg=p, required_size=0, extra_size=p.transaction_size)) to_remove.sort(key=attrgetter('pkg.name')) to_remove = tuple(to_remove) to_upgrade = None if transaction.to_upgrade: to_upgrade = [] for p in transaction.to_upgrade: p_data = update_data.get(p.name) if update_data else None if p_data: req_size = p_data.get('compressed size') else: req_size = None to_upgrade.append(UpgradeRequirement(pkg=p, required_size=req_size, extra_size=p.transaction_size)) to_upgrade.sort(key=attrgetter('pkg.name')) to_upgrade = tuple(to_upgrade) return UpgradeRequirements(to_install=to_install, to_upgrade=to_upgrade, to_remove=to_remove, cannot_upgrade=None) else: cannot_upgrade = [UpgradeRequirement(pkg=p, reason=self._i18n['error']) for p in pkgs] cannot_upgrade.sort(key=attrgetter('pkg.name')) return UpgradeRequirements(cannot_upgrade=cannot_upgrade, to_install=None, to_remove=None, to_upgrade=[])