From 9e55ff13d3a58ea1bacdbfcc01e8f071d53adf4c Mon Sep 17 00:00:00 2001 From: Packit Service Date: Feb 24 2021 16:08:17 +0000 Subject: Prepare for a new update Reverting patches so we can apply the latest update and changes can be seen in the spec file and sources. --- diff --git a/dnf/base.py b/dnf/base.py index c1573d4..8c86acb 100644 --- a/dnf/base.py +++ b/dnf/base.py @@ -28,12 +28,12 @@ import argparse import dnf import libdnf.transaction -from copy import deepcopy from dnf.comps import CompsQuery from dnf.i18n import _, P_, ucd from dnf.util import _parse_specs from dnf.db.history import SwdbInterface from dnf.yum import misc +from functools import reduce try: from collections.abc import Sequence except ImportError: @@ -242,8 +242,6 @@ class Base(object): @property def comps(self): # :api - if self._comps is None: - self.read_comps(arch_filter=True) return self._comps @property @@ -547,7 +545,7 @@ class Base(object): if self.conf.ignorearch: self._rpm_probfilter.add(rpm.RPMPROB_FILTER_IGNOREARCH) - probfilter = functools.reduce(operator.or_, self._rpm_probfilter, 0) + probfilter = reduce(operator.or_, self._rpm_probfilter, 0) self._priv_ts.setProbFilter(probfilter) return self._priv_ts @@ -888,15 +886,6 @@ class Base(object): self._plugins.unload_removed_plugins(self.transaction) self._plugins.run_transaction() - # log post transaction summary - def _pto_callback(action, tsis): - msgs = [] - for tsi in tsis: - msgs.append('{}: {}'.format(action, str(tsi))) - return msgs - for msg in dnf.util._post_transaction_output(self, self.transaction, _pto_callback): - logger.debug(msg) - return tid def _trans_error_summary(self, errstring): @@ -1318,7 +1307,7 @@ class Base(object): if patterns is None or len(patterns) == 0: return list_fn(None) yghs = map(list_fn, patterns) - return functools.reduce(lambda a, b: a.merge_lists(b), yghs) + return reduce(lambda a, b: a.merge_lists(b), yghs) def _list_pattern(self, pkgnarrow, pattern, showdups, ignore_case, reponame=None): @@ -1552,7 +1541,7 @@ class Base(object): if (comps_pkg.basearchonly): query_args.update({'arch': basearch}) q = self.sack.query().filterm(**query_args).apply() - q.filterm(arch__neq=["src", "nosrc"]) + q.filterm(arch__neq="src") if not q: package_string = comps_pkg.name if comps_pkg.basearchonly: @@ -1883,6 +1872,7 @@ class Base(object): no_match_module_specs = install_specs.grp_specs if no_match_module_specs: + self.read_comps(arch_filter=True) exclude_specs.grp_specs = self._expand_groups(exclude_specs.grp_specs) self._install_groups(no_match_module_specs, exclude_specs, no_match_group_specs, strict) @@ -2085,6 +2075,7 @@ class Base(object): msg = _('Not a valid form: %s') logger.warning(msg, grp_spec) elif grp_specs: + self.read_comps(arch_filter=True) if self.env_group_remove(grp_specs): done = True @@ -2218,6 +2209,65 @@ class Base(object): for prefix in ['/bin/', '/sbin/', '/usr/bin/', '/usr/sbin/']] return self.sack.query().filterm(file__glob=binary_provides), binary_provides + def _history_undo_operations(self, operations, first_trans, rollback=False, strict=True): + """Undo the operations on packages by their NEVRAs. + + :param operations: a NEVRAOperations to be undone + :param first_trans: first transaction id being undone + :param rollback: True if transaction is performing a rollback + :param strict: if True, raise an exception on any errors + """ + + # map actions to their opposites + action_map = { + libdnf.transaction.TransactionItemAction_DOWNGRADE: None, + libdnf.transaction.TransactionItemAction_DOWNGRADED: libdnf.transaction.TransactionItemAction_UPGRADE, + libdnf.transaction.TransactionItemAction_INSTALL: libdnf.transaction.TransactionItemAction_REMOVE, + libdnf.transaction.TransactionItemAction_OBSOLETE: None, + libdnf.transaction.TransactionItemAction_OBSOLETED: libdnf.transaction.TransactionItemAction_INSTALL, + libdnf.transaction.TransactionItemAction_REINSTALL: None, + # reinstalls are skipped as they are considered as no-operation from history perspective + libdnf.transaction.TransactionItemAction_REINSTALLED: None, + libdnf.transaction.TransactionItemAction_REMOVE: libdnf.transaction.TransactionItemAction_INSTALL, + libdnf.transaction.TransactionItemAction_UPGRADE: None, + libdnf.transaction.TransactionItemAction_UPGRADED: libdnf.transaction.TransactionItemAction_DOWNGRADE, + libdnf.transaction.TransactionItemAction_REASON_CHANGE: None, + } + + failed = False + for ti in operations.packages(): + try: + action = action_map[ti.action] + except KeyError: + raise RuntimeError(_("Action not handled: {}".format(action))) + + if action is None: + continue + + if action == libdnf.transaction.TransactionItemAction_REMOVE: + query = self.sack.query().installed().filterm(nevra_strict=str(ti)) + if not query: + logger.error(_('No package %s installed.'), ucd(str(ti))) + failed = True + continue + else: + query = self.sack.query().filterm(nevra_strict=str(ti)) + if not query: + logger.error(_('No package %s available.'), ucd(str(ti))) + failed = True + continue + + if action == libdnf.transaction.TransactionItemAction_REMOVE: + for pkg in query: + self._goal.erase(pkg) + else: + selector = dnf.selector.Selector(self.sack) + selector.set(pkg=query) + self._goal.install(select=selector, optional=(not strict)) + + if strict and failed: + raise dnf.exceptions.PackageNotFoundError(_('no package matched')) + def _merge_update_filters(self, q, pkg_spec=None, warning=True): """ Merge Queries in _update_filters and return intersection with q Query @@ -2527,37 +2577,6 @@ class Base(object): """ self._logging._setup_from_dnf_conf(self.conf, file_loggers_only=True) - def _skipped_packages(self, report_problems, transaction): - """returns set of conflicting packages and set of packages with broken dependency that would - be additionally installed when --best and --allowerasing""" - if self._goal.actions & (hawkey.INSTALL | hawkey.UPGRADE | hawkey.UPGRADE_ALL): - best = True - else: - best = False - ng = deepcopy(self._goal) - params = {"allow_uninstall": self._allow_erasing, - "force_best": best, - "ignore_weak": True} - ret = ng.run(**params) - if not ret and report_problems: - msg = dnf.util._format_resolve_problems(ng.problem_rules()) - logger.warning(msg) - problem_conflicts = set(ng.problem_conflicts(available=True)) - problem_dependency = set(ng.problem_broken_dependency(available=True)) - problem_conflicts - - def _nevra(item): - return hawkey.NEVRA(name=item.name, epoch=item.epoch, version=item.version, - release=item.release, arch=item.arch) - - # Sometimes, pkg is not in transaction item, therefore, comparing by nevra - transaction_nevras = [_nevra(tsi) for tsi in transaction] - skipped_conflicts = set( - [pkg for pkg in problem_conflicts if _nevra(pkg) not in transaction_nevras]) - skipped_dependency = set( - [pkg for pkg in problem_dependency if _nevra(pkg) not in transaction_nevras]) - - return skipped_conflicts, skipped_dependency - def _msg_installed(pkg): name = ucd(pkg) diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py index be737ed..b5f7bca 100644 --- a/dnf/cli/cli.py +++ b/dnf/cli/cli.py @@ -251,12 +251,8 @@ class BaseCli(dnf.Base): trans = None if trans: - # the post transaction summary is already written to log during - # Base.do_transaction() so here only print the messages to the - # user arranged in columns - print() - print('\n'.join(self.output.post_transaction_output(trans))) - print() + msg = self.output.post_transaction_output(trans) + logger.info(msg) for tsi in trans: if tsi.state == libdnf.transaction.TransactionItemState_ERROR: raise dnf.exceptions.Error(_('Transaction failed')) @@ -505,7 +501,7 @@ class BaseCli(dnf.Base): # XXX put this into the ListCommand at some point if len(ypl.obsoletes) > 0 and basecmd == 'list': # if we've looked up obsolete lists and it's a list request - rop = len(ypl.obsoletes) + rop = [0, ''] print(_('Obsoleting Packages')) for obtup in sorted(ypl.obsoletesTuples, key=operator.itemgetter(0)): @@ -517,7 +513,8 @@ class BaseCli(dnf.Base): rrap = self.output.listPkgs(ypl.recent, _('Recently Added Packages'), basecmd, columns=columns) if len(patterns) and \ - rrap == 0 and rop == 0 and rup == 0 and rep == 0 and rap == 0 and raep == 0 and rip == 0: + rrap[0] and rop[0] and rup[0] and rep[0] and rap[0] and \ + raep[0] and rip[0]: raise dnf.exceptions.Error(_('No matching Packages to list')) def returnPkgLists(self, pkgnarrow='all', patterns=None, @@ -607,6 +604,109 @@ class BaseCli(dnf.Base): return False return True + def _history_get_transactions(self, extcmds): + if not extcmds: + logger.critical(_('No transaction ID given')) + return None + + old = self.history.old(extcmds) + if not old: + logger.critical(_('Not found given transaction ID')) + return None + return old + + def history_get_transaction(self, extcmds): + old = self._history_get_transactions(extcmds) + if old is None: + return None + if len(old) > 1: + logger.critical(_('Found more than one transaction ID!')) + return old[0] + + def history_rollback_transaction(self, extcmd): + """Rollback given transaction.""" + old = self.history_get_transaction((extcmd,)) + if old is None: + return 1, ['Failed history rollback, no transaction'] + last = self.history.last() + if last is None: + return 1, ['Failed history rollback, no last?'] + if old.tid == last.tid: + return 0, ['Rollback to current, nothing to do'] + + mobj = None + for trans in self.history.old(list(range(old.tid + 1, last.tid + 1))): + if trans.altered_lt_rpmdb: + logger.warning(_('Transaction history is incomplete, before %u.'), trans.tid) + elif trans.altered_gt_rpmdb: + logger.warning(_('Transaction history is incomplete, after %u.'), trans.tid) + + if mobj is None: + mobj = dnf.db.history.MergedTransactionWrapper(trans) + else: + mobj.merge(trans) + + tm = dnf.util.normalize_time(old.beg_timestamp) + print("Rollback to transaction %u, from %s" % (old.tid, tm)) + print(self.output.fmtKeyValFill(" Undoing the following transactions: ", + ", ".join((str(x) for x in mobj.tids())))) + self.output.historyInfoCmdPkgsAltered(mobj) # :todo + +# history = dnf.history.open_history(self.history) # :todo +# m = libdnf.transaction.MergedTransaction() + +# return + +# operations = dnf.history.NEVRAOperations() +# for id_ in range(old.tid + 1, last.tid + 1): +# operations += history.transaction_nevra_ops(id_) + + try: + self._history_undo_operations(mobj, old.tid + 1, True, strict=self.conf.strict) + except dnf.exceptions.PackagesNotInstalledError as err: + raise + logger.info(_('No package %s installed.'), + self.output.term.bold(ucd(err.pkg_spec))) + return 1, ['A transaction cannot be undone'] + except dnf.exceptions.PackagesNotAvailableError as err: + raise + logger.info(_('No package %s available.'), + self.output.term.bold(ucd(err.pkg_spec))) + return 1, ['A transaction cannot be undone'] + except dnf.exceptions.MarkingError: + raise + assert False + else: + return 2, ["Rollback to transaction %u" % (old.tid,)] + + def history_undo_transaction(self, extcmd): + """Undo given transaction.""" + old = self.history_get_transaction((extcmd,)) + if old is None: + return 1, ['Failed history undo'] + + tm = dnf.util.normalize_time(old.beg_timestamp) + msg = _("Undoing transaction {}, from {}").format(old.tid, ucd(tm)) + logger.info(msg) + self.output.historyInfoCmdPkgsAltered(old) # :todo + + + mobj = dnf.db.history.MergedTransactionWrapper(old) + + try: + self._history_undo_operations(mobj, old.tid, strict=self.conf.strict) + except dnf.exceptions.PackagesNotInstalledError as err: + logger.info(_('No package %s installed.'), + self.output.term.bold(ucd(err.pkg_spec))) + return 1, ['An operation cannot be undone'] + except dnf.exceptions.PackagesNotAvailableError as err: + logger.info(_('No package %s available.'), + self.output.term.bold(ucd(err.pkg_spec))) + return 1, ['An operation cannot be undone'] + except dnf.exceptions.MarkingError: + raise + else: + return 2, ["Undoing transaction %u" % (old.tid,)] class Cli(object): def __init__(self, base): @@ -874,8 +974,6 @@ class Cli(object): self.base.configure_plugins() - self.base.conf._configure_from_options(opts) - self.command.configure() if self.base.conf.destdir: diff --git a/dnf/cli/commands/group.py b/dnf/cli/commands/group.py index cf54279..bd17f80 100644 --- a/dnf/cli/commands/group.py +++ b/dnf/cli/commands/group.py @@ -110,6 +110,9 @@ class GroupCommand(commands.Command): return installed, available + def _grp_setup(self): + self.base.read_comps(arch_filter=True) + def _info(self, userlist): for strng in userlist: group_matched = False @@ -367,6 +370,8 @@ class GroupCommand(commands.Command): cmd = self.opts.subcmd extcmds = self.opts.args + self._grp_setup() + if cmd == 'summary': return self._summary(extcmds) if cmd == 'list': diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py index 293d93f..e381f90 100644 --- a/dnf/cli/commands/history.py +++ b/dnf/cli/commands/history.py @@ -20,7 +20,6 @@ from __future__ import print_function from __future__ import unicode_literals import libdnf -import hawkey from dnf.i18n import _, ucd from dnf.cli import commands @@ -121,10 +120,6 @@ class HistoryCommand(commands.Command): if not self.opts.transactions: raise dnf.cli.CliError(_('No transaction ID or package name given.')) elif self.opts.transactions_action in ['redo', 'undo', 'rollback']: - demands.available_repos = True - demands.resolving = True - demands.root_user = True - self._require_one_transaction_id = True if not self.opts.transactions: msg = _('No transaction ID or package name given.') @@ -159,101 +154,48 @@ class HistoryCommand(commands.Command): return dnf.cli.commands.Command.get_error_output(self, error) def _hcmd_redo(self, extcmds): - old = self._history_get_transaction(extcmds) - data = serialize_transaction(old) - self.replay = TransactionReplay( - self.base, - data=data, - ignore_installed=True, - ignore_extras=True, - skip_unavailable=self.opts.skip_unavailable - ) - self.replay.run() - - def _history_get_transactions(self, extcmds): - if not extcmds: - raise dnf.cli.CliError(_('No transaction ID given')) - - old = self.base.history.old(extcmds) - if not old: - raise dnf.cli.CliError(_('Transaction ID "{0}" not found.').format(extcmds[0])) - return old - - def _history_get_transaction(self, extcmds): - old = self._history_get_transactions(extcmds) - if len(old) > 1: - raise dnf.cli.CliError(_('Found more than one transaction ID!')) - return old[0] + old = self.base.history_get_transaction(extcmds) + if old is None: + return 1, ['Failed history redo'] + tm = dnf.util.normalize_time(old.beg_timestamp) + print('Repeating transaction %u, from %s' % (old.tid, tm)) + self.output.historyInfoCmdPkgsAltered(old) + + for i in old.packages(): + pkgs = list(self.base.sack.query().filter(nevra=str(i), reponame=i.from_repo)) + if i.action in dnf.transaction.FORWARD_ACTIONS: + if not pkgs: + logger.info(_('No package %s available.'), + self.output.term.bold(ucd(str(i)))) + return 1, ['An operation cannot be redone'] + pkg = pkgs[0] + self.base.install(str(pkg)) + elif i.action == libdnf.transaction.TransactionItemAction_REMOVE: + if not pkgs: + # package was removed already, we can skip removing it again + continue + pkg = pkgs[0] + self.base.remove(str(pkg)) + + self.base.resolve() + self.base.do_transaction() def _hcmd_undo(self, extcmds): - old = self._history_get_transaction(extcmds) - self._revert_transaction(old) + try: + return self.base.history_undo_transaction(extcmds[0]) + except dnf.exceptions.Error as err: + return 1, [str(err)] def _hcmd_rollback(self, extcmds): - old = self._history_get_transaction(extcmds) - last = self.base.history.last() - - merged_trans = None - if old.tid != last.tid: - # history.old([]) returns all transactions and we don't want that - # so skip merging the transactions when trying to rollback to the last transaction - # which is the current system state and rollback is not applicable - for trans in self.base.history.old(list(range(old.tid + 1, last.tid + 1))): - if trans.altered_lt_rpmdb: - logger.warning(_('Transaction history is incomplete, before %u.'), trans.tid) - elif trans.altered_gt_rpmdb: - logger.warning(_('Transaction history is incomplete, after %u.'), trans.tid) - - if merged_trans is None: - merged_trans = dnf.db.history.MergedTransactionWrapper(trans) - else: - merged_trans.merge(trans) - - self._revert_transaction(merged_trans) - - def _revert_transaction(self, trans): - action_map = { - "Install": "Removed", - "Removed": "Install", - "Upgrade": "Downgraded", - "Upgraded": "Downgrade", - "Downgrade": "Upgraded", - "Downgraded": "Upgrade", - "Reinstalled": "Reinstall", - "Reinstall": "Reinstalled", - "Obsoleted": "Install", - "Obsolete": "Obsoleted", - } - - data = serialize_transaction(trans) - - # revert actions in the serialized transaction data to perform rollback/undo - for content_type in ("rpms", "groups", "environments"): - for ti in data.get(content_type, []): - ti["action"] = action_map[ti["action"]] - - if ti["action"] == "Install" and ti.get("reason", None) == "clean": - ti["reason"] = "dependency" - - if ti.get("repo_id") == hawkey.SYSTEM_REPO_NAME: - # erase repo_id, because it's not possible to perform forward actions from the @System repo - ti["repo_id"] = None - - self.replay = TransactionReplay( - self.base, - data=data, - ignore_installed=True, - ignore_extras=True, - skip_unavailable=self.opts.skip_unavailable - ) - self.replay.run() + try: + return self.base.history_rollback_transaction(extcmds[0]) + except dnf.exceptions.Error as err: + return 1, [str(err)] def _hcmd_userinstalled(self): """Execute history userinstalled command.""" pkgs = tuple(self.base.iter_userinstalled()) - n_listed = self.output.listPkgs(pkgs, 'Packages installed by user', 'nevra') - if n_listed == 0: - raise dnf.cli.CliError(_('No packages to list')) + return self.output.listPkgs(pkgs, 'Packages installed by user', 'nevra') def _args2transaction_ids(self): """Convert commandline arguments to transaction ids""" @@ -321,11 +263,14 @@ class HistoryCommand(commands.Command): def run(self): vcmd = self.opts.transactions_action + ret = None if vcmd == 'replay': + self.base.read_comps(arch_filter=True) + self.replay = TransactionReplay( self.base, - filename=self.opts.transaction_filename, + self.opts.transaction_filename, ignore_installed = self.opts.ignore_installed, ignore_extras = self.opts.ignore_extras, skip_unavailable = self.opts.skip_unavailable @@ -335,20 +280,23 @@ class HistoryCommand(commands.Command): tids, merged_tids = self._args2transaction_ids() if vcmd == 'list' and (tids or not self.opts.transactions): - self.output.historyListCmd(tids, reverse=self.opts.reverse) + ret = self.output.historyListCmd(tids, reverse=self.opts.reverse) elif vcmd == 'info' and (tids or not self.opts.transactions): - self.output.historyInfoCmd(tids, self.opts.transactions, merged_tids) + ret = self.output.historyInfoCmd(tids, self.opts.transactions, merged_tids) elif vcmd == 'undo': - self._hcmd_undo(tids) + ret = self._hcmd_undo(tids) elif vcmd == 'redo': - self._hcmd_redo(tids) + ret = self._hcmd_redo(tids) elif vcmd == 'rollback': - self._hcmd_rollback(tids) + ret = self._hcmd_rollback(tids) elif vcmd == 'userinstalled': - self._hcmd_userinstalled() + ret = self._hcmd_userinstalled() elif vcmd == 'store': - tid = self._history_get_transaction(tids) - data = serialize_transaction(tid) + transactions = self.output.history.old(tids) + if not transactions: + raise dnf.cli.CliError(_('Transaction ID "{id}" not found.').format(id=tids[0])) + + data = serialize_transaction(transactions[0]) try: filename = self.opts.output if self.opts.output is not None else "transaction.json" @@ -369,21 +317,29 @@ class HistoryCommand(commands.Command): except OSError as e: raise dnf.cli.CliError(_('Error storing transaction: {}').format(str(e))) + if ret is None: + return + (code, strs) = ret + if code == 2: + self.cli.demands.resolving = True + elif code != 0: + raise dnf.exceptions.Error(strs[0]) + def run_resolved(self): - if self.opts.transactions_action not in ("replay", "redo", "rollback", "undo"): + if self.opts.transactions_action != "replay": return self.replay.post_transaction() def run_transaction(self): - if self.opts.transactions_action not in ("replay", "redo", "rollback", "undo"): + if self.opts.transactions_action != "replay": return warnings = self.replay.get_warnings() if warnings: logger.log( dnf.logging.WARNING, - _("Warning, the following problems occurred while running a transaction:") + _("Warning, the following problems occurred while replaying the transaction:") ) for w in warnings: logger.log(dnf.logging.WARNING, " " + w) diff --git a/dnf/cli/commands/install.py b/dnf/cli/commands/install.py index b637af0..38a90b6 100644 --- a/dnf/cli/commands/install.py +++ b/dnf/cli/commands/install.py @@ -151,6 +151,7 @@ class InstallCommand(commands.Command): return err_pkgs def _install_groups(self, grp_specs): + self.base.read_comps(arch_filter=True) try: self.base.env_group_install(grp_specs, tuple(self.base.conf.group_package_types), diff --git a/dnf/cli/commands/remove.py b/dnf/cli/commands/remove.py index e455ba6..f50dbd9 100644 --- a/dnf/cli/commands/remove.py +++ b/dnf/cli/commands/remove.py @@ -142,6 +142,7 @@ class RemoveCommand(commands.Command): skipped_grps = self.opts.grp_specs if skipped_grps: + self.base.read_comps(arch_filter=True) for group in skipped_grps: try: if self.base.env_group_remove([group]): diff --git a/dnf/cli/commands/repoquery.py b/dnf/cli/commands/repoquery.py index b0d06a9..099a931 100644 --- a/dnf/cli/commands/repoquery.py +++ b/dnf/cli/commands/repoquery.py @@ -632,6 +632,7 @@ class RepoQueryCommand(commands.Command): print("\n".join(sorted(pkgs))) def _group_member_report(self, query): + self.base.read_comps(arch_filter=True) package_conf_dict = {} for group in self.base.comps.groups: package_conf_dict[group.id] = set([pkg.name for pkg in group.packages_iter()]) diff --git a/dnf/cli/commands/shell.py b/dnf/cli/commands/shell.py index 18c886f..431fe50 100644 --- a/dnf/cli/commands/shell.py +++ b/dnf/cli/commands/shell.py @@ -239,9 +239,6 @@ exit (or quit) exit the shell""") if fill_sack: self.base.fill_sack() - # reset base._comps, as it has changed due to changing the repos - self.base._comps = None - else: self._help('repo') diff --git a/dnf/cli/commands/upgrade.py b/dnf/cli/commands/upgrade.py index f62cfcc..44789c9 100644 --- a/dnf/cli/commands/upgrade.py +++ b/dnf/cli/commands/upgrade.py @@ -124,6 +124,7 @@ class UpgradeCommand(commands.Command): def _update_groups(self): if self.skipped_grp_specs: + self.base.read_comps(arch_filter=True) self.base.env_group_upgrade(self.skipped_grp_specs) return True return False diff --git a/dnf/cli/output.py b/dnf/cli/output.py index 6cfc9e2..51d6829 100644 --- a/dnf/cli/output.py +++ b/dnf/cli/output.py @@ -21,7 +21,9 @@ from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals +from copy import deepcopy import fnmatch +import functools import hawkey import itertools import libdnf.transaction @@ -35,7 +37,7 @@ import time from dnf.cli.format import format_number, format_time from dnf.i18n import _, C_, P_, ucd, fill_exact_width, textwrap_fill, exact_width, select_short_long from dnf.pycomp import xrange, basestring, long, unicode, sys_maxsize -from dnf.yum.rpmtrans import TransactionDisplay +from dnf.yum.rpmtrans import LoggingTransactionDisplay from dnf.db.history import MergedTransactionWrapper import dnf.base import dnf.callback @@ -50,6 +52,50 @@ import dnf.yum.misc logger = logging.getLogger('dnf') +def _make_lists(transaction, goal): + b = dnf.util.Bunch({ + 'downgraded': [], + 'erased': [], + 'erased_clean': [], + 'erased_dep': [], + 'installed': [], + 'installed_group': [], + 'installed_dep': [], + 'installed_weak': [], + 'reinstalled': [], + 'upgraded': [], + 'failed': [], + }) + + for tsi in transaction: + if tsi.state == libdnf.transaction.TransactionItemState_ERROR: + b.failed.append(tsi) + elif tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADE: + b.downgraded.append(tsi) + elif tsi.action == libdnf.transaction.TransactionItemAction_INSTALL: + if tsi.reason == libdnf.transaction.TransactionItemReason_GROUP: + b.installed_group.append(tsi) + elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: + b.installed_dep.append(tsi) + elif tsi.reason == libdnf.transaction.TransactionItemReason_WEAK_DEPENDENCY: + b.installed_weak.append(tsi) + else: + # TransactionItemReason_USER + b.installed.append(tsi) + elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALL: + b.reinstalled.append(tsi) + elif tsi.action == libdnf.transaction.TransactionItemAction_REMOVE: + if tsi.reason == libdnf.transaction.TransactionItemReason_CLEAN: + b.erased_clean.append(tsi) + elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: + b.erased_dep.append(tsi) + else: + b.erased.append(tsi) + elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADE: + b.upgraded.append(tsi) + + return b + def _spread_in_columns(cols_count, label, lst): left = itertools.chain((label,), itertools.repeat('')) @@ -597,10 +643,18 @@ class Output(object): number '>' - highlighting used when the package has a higher version number - :return: number of packages listed + :return: (exit_code, [errors]) + + exit_code is:: + + 0 = we're done, exit + 1 = we've errored, exit with error string + """ if outputType in ['list', 'info', 'name', 'nevra']: + thingslisted = 0 if len(lst) > 0: + thingslisted = 1 print('%s' % description) info_set = set() if outputType == 'list': @@ -637,7 +691,9 @@ class Output(object): if info_set: print("\n".join(sorted(info_set))) - return len(lst) + if thingslisted == 0: + return 1, [_('No packages to list')] + return 0, [] def userconfirm(self, msg=None, defaultyes_msg=None): """Get a yes or no from the user, and default to No @@ -1000,6 +1056,37 @@ class Output(object): out[0:0] = self._banner(col_data, (_('Group'), _('Packages'), '', '')) return '\n'.join(out) + def _skipped_packages(self, report_problems, transaction): + """returns set of conflicting packages and set of packages with broken dependency that would + be additionally installed when --best and --allowerasing""" + if self.base._goal.actions & (hawkey.INSTALL | hawkey.UPGRADE | hawkey.UPGRADE_ALL): + best = True + else: + best = False + ng = deepcopy(self.base._goal) + params = {"allow_uninstall": self.base._allow_erasing, + "force_best": best, + "ignore_weak": True} + ret = ng.run(**params) + if not ret and report_problems: + msg = dnf.util._format_resolve_problems(ng.problem_rules()) + logger.warning(msg) + problem_conflicts = set(ng.problem_conflicts(available=True)) + problem_dependency = set(ng.problem_broken_dependency(available=True)) - problem_conflicts + + def _nevra(item): + return hawkey.NEVRA(name=item.name, epoch=item.epoch, version=item.version, + release=item.release, arch=item.arch) + + # Sometimes, pkg is not in transaction item, therefore, comparing by nevra + transaction_nevras = [_nevra(tsi) for tsi in transaction] + skipped_conflicts = set( + [pkg for pkg in problem_conflicts if _nevra(pkg) not in transaction_nevras]) + skipped_dependency = set( + [pkg for pkg in problem_dependency if _nevra(pkg) not in transaction_nevras]) + + return skipped_conflicts, skipped_dependency + def list_transaction(self, transaction, total_width=None): """Return a string representation of the transaction in an easy-to-read format. @@ -1014,7 +1101,7 @@ class Output(object): # in order to display module changes when RPM transaction is empty transaction = [] - list_bunch = dnf.util._make_lists(transaction) + list_bunch = _make_lists(transaction, self.base._goal) pkglist_lines = [] data = {'n' : {}, 'v' : {}, 'r' : {}} a_wid = 0 # Arch can't get "that big" ... so always use the max. @@ -1183,7 +1270,7 @@ class Output(object): # show skipped conflicting packages if not self.conf.best and self.base._goal.actions & forward_actions: lines = [] - skipped_conflicts, skipped_broken = self.base._skipped_packages( + skipped_conflicts, skipped_broken = self._skipped_packages( report_problems=True, transaction=transaction) skipped_broken = dict((str(pkg), pkg) for pkg in skipped_broken) for pkg in sorted(skipped_conflicts): @@ -1348,8 +1435,13 @@ Transaction Summary max_msg_count, count, msg_pkgs)) return ''.join(out) + def post_transaction_output(self, transaction): + """Returns a human-readable summary of the results of the + transaction. - def _pto_callback(self, action, tsis): + :return: a string containing a human-readable summary of the + results of the transaction + """ # Works a bit like calcColumns, but we never overflow a column we just # have a dynamic number of columns. def _fits_in_cols(msgs, num): @@ -1379,32 +1471,60 @@ Transaction Summary col_lens[col] *= -1 return col_lens - if not tsis: - return '' - out = [] - msgs = [] - out.append('{}:'.format(action)) - for tsi in tsis: - msgs.append(str(tsi)) - for num in (8, 7, 6, 5, 4, 3, 2): - cols = _fits_in_cols(msgs, num) - if cols: - break - if not cols: - cols = [-(self.term.columns - 2)] - while msgs: - current_msgs = msgs[:len(cols)] - out.append(' {}'.format(self.fmtColumns(zip(current_msgs, cols)))) - msgs = msgs[len(cols):] - return out - + def _tsi_or_pkg_nevra_cmp(item1, item2): + """Compares two transaction items or packages by nevra. + Used as a fallback when tsi does not contain package object. + """ + ret = (item1.name > item2.name) - (item1.name < item2.name) + if ret != 0: + return ret + nevra1 = hawkey.NEVRA(name=item1.name, epoch=item1.epoch, version=item1.version, + release=item1.release, arch=item1.arch) + nevra2 = hawkey.NEVRA(name=item2.name, epoch=item2.epoch, version=item2.version, + release=item2.release, arch=item2.arch) + ret = nevra1.evr_cmp(nevra2, self.sack) + if ret != 0: + return ret + return (item1.arch > item2.arch) - (item1.arch < item2.arch) + + out = '' + list_bunch = _make_lists(transaction, self.base._goal) + + skipped_conflicts, skipped_broken = self._skipped_packages( + report_problems=False, transaction=transaction) + skipped = skipped_conflicts.union(skipped_broken) + + for (action, tsis) in [(_('Upgraded'), list_bunch.upgraded), + (_('Downgraded'), list_bunch.downgraded), + (_('Installed'), list_bunch.installed + + list_bunch.installed_group + + list_bunch.installed_weak + + list_bunch.installed_dep), + (_('Reinstalled'), list_bunch.reinstalled), + (_('Skipped'), skipped), + (_('Removed'), list_bunch.erased + + list_bunch.erased_dep + + list_bunch.erased_clean), + (_('Failed'), list_bunch.failed)]: + if not tsis: + continue + msgs = [] + out += '\n%s:\n' % action + for tsi in sorted(tsis, key=functools.cmp_to_key(_tsi_or_pkg_nevra_cmp)): + msgs.append(str(tsi)) + for num in (8, 7, 6, 5, 4, 3, 2): + cols = _fits_in_cols(msgs, num) + if cols: + break + if not cols: + cols = [-(self.term.columns - 2)] + while msgs: + current_msgs = msgs[:len(cols)] + out += ' ' + out += self.fmtColumns(zip(current_msgs, cols), end=u'\n') + msgs = msgs[len(cols):] - def post_transaction_output(self, transaction): - """ - Return a human-readable summary of the transaction. Packages in sections - are arranged to columns. - """ - return dnf.util._post_transaction_output(self.base, transaction, self._pto_callback) + return out def setup_progress_callbacks(self): """Set up the progress callbacks and various @@ -1867,6 +1987,10 @@ Transaction Summary class DepSolveProgressCallBack(dnf.callback.Depsolve): """Provides text output callback functions for Dependency Solver callback.""" + def __init__(self): + """requires yum-cli log and errorlog functions as arguments""" + self.loops = 0 + def pkg_added(self, pkg, mode): """Print information about a package being added to the transaction set. @@ -1913,6 +2037,7 @@ class DepSolveProgressCallBack(dnf.callback.Depsolve): process. """ logger.debug(_('--> Starting dependency resolution')) + self.loops += 1 def end(self): """Output a message stating that dependency resolution has finished.""" @@ -1946,7 +2071,7 @@ class CliKeyImport(dnf.callback.KeyImport): return self.output.userconfirm() -class CliTransactionDisplay(TransactionDisplay): +class CliTransactionDisplay(LoggingTransactionDisplay): """A YUM specific callback class for RPM operations.""" width = property(lambda self: dnf.cli.term._term_width()) @@ -1968,7 +2093,7 @@ class CliTransactionDisplay(TransactionDisplay): :param package: the package involved in the event :param action: the type of action that is taking place. Valid values are given by - :func:`rpmtrans.TransactionDisplay.action.keys()` + :func:`rpmtrans.LoggingTransactionDisplay.action.keys()` :param ti_done: a number representing the amount of work already done in the current transaction :param ti_total: a number representing the total amount of work @@ -2019,6 +2144,20 @@ class CliTransactionDisplay(TransactionDisplay): if ti_done == ti_total: print(" ") + def filelog(self, package, action): + pass + + def error(self, message): + pass + + def scriptout(self, msgs): + """Print messages originating from a package script. + + :param msgs: the messages coming from the script + """ + if msgs: + self.rpm_logger.info(ucd(msgs)) + def _makefmt(self, percent, ts_done, ts_total, progress=True, pkgname=None, wid1=15): l = len(str(ts_total)) diff --git a/dnf/db/history.py b/dnf/db/history.py index 994cdb0..4d355f9 100644 --- a/dnf/db/history.py +++ b/dnf/db/history.py @@ -381,9 +381,6 @@ class SwdbInterface(object): prev_trans.altered_gt_rpmdb = True return result[::-1] - def get_current(self): - return TransactionWrapper(self.swdb.getCurrent()) - def set_reason(self, pkg, reason): """Set reason for package""" rpm_item = self.rpm._pkg_to_swdb_rpm_item(pkg) diff --git a/dnf/module/module_base.py b/dnf/module/module_base.py index 49c871c..04701b9 100644 --- a/dnf/module/module_base.py +++ b/dnf/module/module_base.py @@ -140,21 +140,20 @@ class ModuleBase(object): if fail_safe_repo_used: raise dnf.exceptions.Error(_( "Installing module from Fail-Safe repository is not allowed")) - # Remove source packages they cannot be installed or upgraded - base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() - install_base_query = base_no_source_query.filter(nevra_strict=install_set_artefacts) + install_base_query = self.base.sack.query().filterm( + nevra_strict=install_set_artefacts).apply() # add hot-fix packages hot_fix_repos = [i.id for i in self.base.repos.iter_enabled() if i.module_hotfixes] - hotfix_packages = base_no_source_query.filter( - reponame=hot_fix_repos, name=install_dict.keys()) + hotfix_packages = self.base.sack.query().filterm(reponame=hot_fix_repos).filterm( + name=install_dict.keys()) install_base_query = install_base_query.union(hotfix_packages) for pkg_name, set_specs in install_dict.items(): query = install_base_query.filter(name=pkg_name) if not query: # package can also be non-modular or part of another stream - query = base_no_source_query.filter(name=pkg_name) + query = self.base.sack.query().filterm(name=pkg_name) if not query: for spec in set_specs: logger.error(_("Unable to resolve argument {}").format(spec)) @@ -183,9 +182,6 @@ class ModuleBase(object): fail_safe_repo = hawkey.MODULE_FAIL_SAFE_REPO_NAME fail_safe_repo_used = False - # Remove source packages they cannot be installed or upgraded - base_no_source_query = self.base.sack.query().filterm(arch__neq=['src', 'nosrc']).apply() - for spec in module_specs: module_list, nsvcap = self._get_modules(spec) if not module_list: @@ -225,7 +221,7 @@ class ModuleBase(object): if not upgrade_package_set: logger.error(_("Unable to match profile in argument {}").format(spec)) - query = base_no_source_query.filter(name=upgrade_package_set) + query = self.base.sack.query().filterm(name=upgrade_package_set) if query: sltr = dnf.selector.Selector(self.base.sack) sltr.set(pkg=query) diff --git a/dnf/transaction_sr.py b/dnf/transaction_sr.py index dae8d30..9b9b074 100644 --- a/dnf/transaction_sr.py +++ b/dnf/transaction_sr.py @@ -48,32 +48,30 @@ class TransactionError(dnf.exceptions.Error): super(TransactionError, self).__init__(msg) -class TransactionReplayError(dnf.exceptions.Error): +class TransactionFileError(dnf.exceptions.Error): def __init__(self, filename, errors): """ :param filename: The name of the transaction file being replayed :param errors: a list of error classes or a string with an error description """ - # store args in case someone wants to read them from a caught exception - self.filename = filename if isinstance(errors, (list, tuple)): - self.errors = errors - else: - self.errors = [errors] + if len(errors) > 1: + msg = _('Errors in "{filename}":').format(filename=filename) + for error in errors: + msg += "\n " + str(error) - if filename: - msg = _('The following problems occurred while replaying the transaction from file "{filename}":').format(filename=filename) - else: - msg = _('The following problems occurred while running a transaction:') + super(TransactionFileError, self).__init__(msg) + return - for error in self.errors: - msg += "\n " + str(error) + else: + errors = str(errors[0]) - super(TransactionReplayError, self).__init__(msg) + msg = _('Error in "{filename}": {error}').format(filename=filename, error=errors) + super(TransactionFileError, self).__init__(msg) -class IncompatibleTransactionVersionError(TransactionReplayError): +class IncompatibleTransactionVersionError(TransactionFileError): def __init__(self, filename, msg): super(IncompatibleTransactionVersionError, self).__init__(filename, msg) @@ -84,7 +82,7 @@ def _check_version(version, filename): try: major = int(major) except ValueError as e: - raise TransactionReplayError( + raise TransactionFileError( filename, _('Invalid major version "{major}", number expected.').format(major=major) ) @@ -92,7 +90,7 @@ def _check_version(version, filename): try: int(minor) # minor is unused, just check it's a number except ValueError as e: - raise TransactionReplayError( + raise TransactionFileError( filename, _('Invalid minor version "{minor}", number expected.').format(minor=minor) ) @@ -118,9 +116,6 @@ def serialize_transaction(transaction): groups = [] environments = [] - if transaction is None: - return data - for tsi in transaction.packages(): if tsi.is_package(): rpms.append({ @@ -192,23 +187,21 @@ class TransactionReplay(object): def __init__( self, base, - filename="", - data=None, + fn, ignore_extras=False, ignore_installed=False, skip_unavailable=False ): """ :param base: the dnf base - :param filename: the filename to load the transaction from (conflicts with the 'data' argument) - :param data: the dictionary to load the transaction from (conflicts with the 'filename' argument) + :param fn: the filename to load the transaction from :param ignore_extras: whether to ignore extra package pulled into the transaction :param ignore_installed: whether to ignore installed versions of packages :param skip_unavailable: whether to skip transaction packages that aren't available """ self._base = base - self._filename = filename + self._filename = fn self._ignore_installed = ignore_installed self._ignore_extras = ignore_extras self._skip_unavailable = skip_unavailable @@ -220,39 +213,25 @@ class TransactionReplay(object): self._nevra_reason_cache = {} self._warnings = [] - if filename and data: - raise ValueError(_("Conflicting TransactionReplay arguments have been specified: filename, data")) - elif filename: - self._load_from_file(filename) - else: - self._load_from_data(data) - - - def _load_from_file(self, fn): - self._filename = fn with open(fn, "r") as f: try: - replay_data = json.load(f) + self._replay_data = json.load(f) except json.decoder.JSONDecodeError as e: - raise TransactionReplayError(fn, str(e) + ".") + raise TransactionFileError(fn, str(e) + ".") try: - self._load_from_data(replay_data) - except TransactionError as e: - raise TransactionReplayError(fn, e) - - def _load_from_data(self, data): - self._replay_data = data - self._verify_toplevel_json(self._replay_data) + self._verify_toplevel_json(self._replay_data) - self._rpms = self._replay_data.get("rpms", []) - self._assert_type(self._rpms, list, "rpms", "array") + self._rpms = self._replay_data.get("rpms", []) + self._assert_type(self._rpms, list, "rpms", "array") - self._groups = self._replay_data.get("groups", []) - self._assert_type(self._groups, list, "groups", "array") + self._groups = self._replay_data.get("groups", []) + self._assert_type(self._groups, list, "groups", "array") - self._environments = self._replay_data.get("environments", []) - self._assert_type(self._environments, list, "environments", "array") + self._environments = self._replay_data.get("environments", []) + self._assert_type(self._environments, list, "environments", "array") + except TransactionError as e: + raise TransactionFileError(fn, e) def _raise_or_warn(self, warn_only, msg): if warn_only: @@ -268,7 +247,7 @@ class TransactionReplay(object): fn = self._filename if "version" not in replay_data: - raise TransactionReplayError(fn, _('Missing key "{key}".'.format(key="version"))) + raise TransactionFileError(fn, _('Missing key "{key}".'.format(key="version"))) self._assert_type(replay_data["version"], str, "version", "string") @@ -278,7 +257,6 @@ class TransactionReplay(object): try: action = pkg_data["action"] nevra = pkg_data["nevra"] - repo_id = pkg_data["repo_id"] reason = libdnf.transaction.StringToTransactionItemReason(pkg_data["reason"]) except KeyError as e: raise TransactionError( @@ -304,19 +282,6 @@ class TransactionReplay(object): epoch = parsed_nevra.epoch if parsed_nevra.epoch is not None else 0 query = query_na.filter(epoch=epoch, version=parsed_nevra.version, release=parsed_nevra.release) - # In case the package is found in the same repo as in the original - # transaction, limit the query to that plus installed packages. IOW - # remove packages with the same NEVRA in case they are found in - # multiple repos and the repo the package came from originally is one - # of them. - # This can e.g. make a difference in the system-upgrade plugin, in case - # the same NEVRA is in two repos, this makes sure the same repo is used - # for both download and upgrade steps of the plugin. - if repo_id: - query_repo = query.filter(reponame=repo_id) - if query_repo: - query = query_repo.union(query.installed()) - if not query: self._raise_or_warn(self._skip_unavailable, _('Cannot find rpm nevra "{nevra}".').format(nevra=nevra)) return @@ -334,6 +299,7 @@ class TransactionReplay(object): if action == "Install" and query_na.installed() and not self._base._get_installonly_query(query_na): self._raise_or_warn(self._ignore_installed, _('Package "{na}" is already installed for action "{action}".').format(na=na, action=action)) + return sltr = dnf.selector.Selector(self._base.sack).set(pkg=query) self._base.goal.install(select=sltr, optional=not self._base.conf.strict) @@ -579,7 +545,7 @@ class TransactionReplay(object): errors.append(e) if errors: - raise TransactionReplayError(fn, errors) + raise TransactionFileError(fn, errors) def post_transaction(self): """ @@ -634,4 +600,4 @@ class TransactionReplay(object): pass if errors: - raise TransactionReplayError(self._filename, errors) + raise TransactionFileError(self._filename, errors) diff --git a/dnf/util.py b/dnf/util.py index 0beb044..8cf3627 100644 --- a/dnf/util.py +++ b/dnf/util.py @@ -24,14 +24,13 @@ from __future__ import unicode_literals from .pycomp import PY3, basestring from dnf.i18n import _, ucd +from functools import reduce import argparse import dnf import dnf.callback import dnf.const import dnf.pycomp import errno -import functools -import hawkey import itertools import locale import logging @@ -42,7 +41,6 @@ import sys import tempfile import time import libdnf.repo -import libdnf.transaction logger = logging.getLogger('dnf') @@ -197,7 +195,7 @@ def group_by_filter(fn, iterable): def splitter(acc, item): acc[not bool(fn(item))].append(item) return acc - return functools.reduce(splitter, iterable, ([], [])) + return reduce(splitter, iterable, ([], [])) def insert_if(item, iterable, condition): """Insert an item into an iterable by a condition.""" @@ -506,99 +504,3 @@ class MultiCallList(list): def setter(item): setattr(item, what, val) return list(map(setter, self)) - - -def _make_lists(transaction): - b = Bunch({ - 'downgraded': [], - 'erased': [], - 'erased_clean': [], - 'erased_dep': [], - 'installed': [], - 'installed_group': [], - 'installed_dep': [], - 'installed_weak': [], - 'reinstalled': [], - 'upgraded': [], - 'failed': [], - }) - - for tsi in transaction: - if tsi.state == libdnf.transaction.TransactionItemState_ERROR: - b.failed.append(tsi) - elif tsi.action == libdnf.transaction.TransactionItemAction_DOWNGRADE: - b.downgraded.append(tsi) - elif tsi.action == libdnf.transaction.TransactionItemAction_INSTALL: - if tsi.reason == libdnf.transaction.TransactionItemReason_GROUP: - b.installed_group.append(tsi) - elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: - b.installed_dep.append(tsi) - elif tsi.reason == libdnf.transaction.TransactionItemReason_WEAK_DEPENDENCY: - b.installed_weak.append(tsi) - else: - # TransactionItemReason_USER - b.installed.append(tsi) - elif tsi.action == libdnf.transaction.TransactionItemAction_REINSTALL: - b.reinstalled.append(tsi) - elif tsi.action == libdnf.transaction.TransactionItemAction_REMOVE: - if tsi.reason == libdnf.transaction.TransactionItemReason_CLEAN: - b.erased_clean.append(tsi) - elif tsi.reason == libdnf.transaction.TransactionItemReason_DEPENDENCY: - b.erased_dep.append(tsi) - else: - b.erased.append(tsi) - elif tsi.action == libdnf.transaction.TransactionItemAction_UPGRADE: - b.upgraded.append(tsi) - - return b - - -def _post_transaction_output(base, transaction, action_callback): - """Returns a human-readable summary of the results of the - transaction. - - :param action_callback: function generating output for specific action. It - takes two parameters - action as a string and list of affected packages for - this action - :return: a list of lines containing a human-readable summary of the - results of the transaction - """ - def _tsi_or_pkg_nevra_cmp(item1, item2): - """Compares two transaction items or packages by nevra. - Used as a fallback when tsi does not contain package object. - """ - ret = (item1.name > item2.name) - (item1.name < item2.name) - if ret != 0: - return ret - nevra1 = hawkey.NEVRA(name=item1.name, epoch=item1.epoch, version=item1.version, - release=item1.release, arch=item1.arch) - nevra2 = hawkey.NEVRA(name=item2.name, epoch=item2.epoch, version=item2.version, - release=item2.release, arch=item2.arch) - ret = nevra1.evr_cmp(nevra2, base.sack) - if ret != 0: - return ret - return (item1.arch > item2.arch) - (item1.arch < item2.arch) - - list_bunch = dnf.util._make_lists(transaction) - - skipped_conflicts, skipped_broken = base._skipped_packages( - report_problems=False, transaction=transaction) - skipped = skipped_conflicts.union(skipped_broken) - - out = [] - for (action, tsis) in [(_('Upgraded'), list_bunch.upgraded), - (_('Downgraded'), list_bunch.downgraded), - (_('Installed'), list_bunch.installed + - list_bunch.installed_group + - list_bunch.installed_weak + - list_bunch.installed_dep), - (_('Reinstalled'), list_bunch.reinstalled), - (_('Skipped'), skipped), - (_('Removed'), list_bunch.erased + - list_bunch.erased_dep + - list_bunch.erased_clean), - (_('Failed'), list_bunch.failed)]: - out.extend(action_callback( - action, sorted(tsis, key=functools.cmp_to_key(_tsi_or_pkg_nevra_cmp)))) - - return out diff --git a/dnf/yum/rpmtrans.py b/dnf/yum/rpmtrans.py index 51fa921..447639a 100644 --- a/dnf/yum/rpmtrans.py +++ b/dnf/yum/rpmtrans.py @@ -113,10 +113,7 @@ class TransactionDisplay(object): pass def scriptout(self, msgs): - """Hook for reporting an rpm scriptlet output. - - :param msgs: the scriptlet output - """ + """msgs is the messages that were output (if any).""" pass def error(self, message): @@ -143,7 +140,7 @@ class ErrorTransactionDisplay(TransactionDisplay): dnf.util._terminal_messenger('print', message, sys.stderr) -class LoggingTransactionDisplay(TransactionDisplay): +class LoggingTransactionDisplay(ErrorTransactionDisplay): ''' Base class for a RPMTransaction display callback class ''' @@ -159,10 +156,6 @@ class LoggingTransactionDisplay(TransactionDisplay): msg = '%s: %s' % (action_str, package) self.rpm_logger.log(dnf.logging.SUBDEBUG, msg) - def scriptout(self, msgs): - if msgs: - self.rpm_logger.info(ucd(msgs)) - class RPMTransaction(object): def __init__(self, base, test=False, displays=()): diff --git a/doc/command_ref.rst b/doc/command_ref.rst index 8387901..04a24fc 100644 --- a/doc/command_ref.rst +++ b/doc/command_ref.rst @@ -1749,10 +1749,8 @@ The following patterns are supported: those two characters, inclusive, is matched. If the first character following the ``[`` is a ``!`` or a ``^`` then any character not enclosed is matched. - -Note: Curly brackets (``{}``) are not supported. You can still use them in -shells that support them and let the shell do the expansion, but if quoted or -escaped, dnf will not expand them. +``{}`` + Matches any of the comma separated list of enclosed strings. -------------- NEVRA Matching diff --git a/tests/api/test_dnf_base.py b/tests/api/test_dnf_base.py index 656bd22..b1cf49f 100644 --- a/tests/api/test_dnf_base.py +++ b/tests/api/test_dnf_base.py @@ -14,7 +14,6 @@ from .common import TOUR_4_4 class DnfBaseApiTest(TestCase): def setUp(self): self.base = dnf.Base(dnf.conf.Conf()) - self.base.conf.persistdir = "/tmp/tests" def tearDown(self): self.base.close() @@ -34,7 +33,9 @@ class DnfBaseApiTest(TestCase): def test_comps(self): # Base.comps self.assertHasAttr(self.base, "comps") - self.assertHasType(self.base.comps, dnf.comps.Comps) + + # blank initially + self.assertEqual(self.base.comps, None) self.base.read_comps() self.assertHasType(self.base.comps, dnf.comps.Comps) diff --git a/tests/api/test_dnf_db_group.py b/tests/api/test_dnf_db_group.py index e1828cb..447fd12 100644 --- a/tests/api/test_dnf_db_group.py +++ b/tests/api/test_dnf_db_group.py @@ -12,7 +12,6 @@ from .common import TestCase class DnfRPMTransactionApiTest(TestCase): def setUp(self): self.base = dnf.Base(dnf.conf.Conf()) - self.base.conf.persistdir = "/tmp/tests" self.base.fill_sack(False, False) self.base.resolve() self.rpmTrans = self.base.transaction diff --git a/tests/api/test_dnf_module_base.py b/tests/api/test_dnf_module_base.py index 18dd080..aa47555 100644 --- a/tests/api/test_dnf_module_base.py +++ b/tests/api/test_dnf_module_base.py @@ -7,26 +7,16 @@ from __future__ import unicode_literals import dnf import dnf.module.module_base -import os -import shutil -import tempfile - from .common import TestCase class DnfModuleBaseApiTest(TestCase): def setUp(self): self.base = dnf.Base(dnf.conf.Conf()) - self._installroot = tempfile.mkdtemp(prefix="dnf_test_installroot_") - self.base.conf.installroot = self._installroot - self.base.conf.cachedir = os.path.join(self._installroot, "var/cache/dnf") - self.base._sack = dnf.sack._build_sack(self.base) self.moduleBase = dnf.module.module_base.ModuleBase(self.base) def tearDown(self): self.base.close() - if self._installroot.startswith("/tmp/"): - shutil.rmtree(self._installroot) def test_init(self): moduleBase = dnf.module.module_base.ModuleBase(self.base) @@ -61,7 +51,12 @@ class DnfModuleBaseApiTest(TestCase): def test_install(self): # ModuleBase.install() self.assertHasAttr(self.moduleBase, "install") - self.moduleBase.install(module_specs=[], strict=False) + self.assertRaises( + AttributeError, + self.moduleBase.install, + module_specs=[], + strict=False, + ) def test_remove(self): # ModuleBase.remove()