def _fmtColumns(self, columns, msg=u'', end=u'', text_width=utf8_width): """Return a row of data formatted into a string for output. Items can overflow their columns. :param columns: a list of tuples containing the data to output. Each tuple contains first the item to be output, then the amount of space allocated for the column, and then optionally a type of highlighting for the item :param msg: a string to begin the line of output with :param end: a string to end the line of output with :param text_width: a function to find the width of the items in the columns. This defaults to utf8 but can be changed to len() if you know it'll be fine :return: a row of data formatted into a string for output """ total_width = len(msg) data = [] for col_data in columns[:-1]: (val, width) = col_data if not width: # Don't count this column, invisible text msg += u"%s" data.append(val) continue (align, width) = self._fmt_column_align_width(width) val_width = text_width(val) if val_width <= width: # Don't use utf8_width_fill() because it sucks performance # wise for 1,000s of rows. Also allows us to use len(), when # we can. msg += u"%s%s " if (align == u'-'): data.extend([val, " " * (width - val_width)]) else: data.extend([" " * (width - val_width), val]) else: msg += u"%s\n" + " " * (total_width + width + 1) data.append(val) total_width += width total_width += 1 (val, width) = columns[-1] (align, width) = self._fmt_column_align_width(width) val = utf8_width_fill(val, width, left=(align == u'-')) msg += u"%%s%s" % end data.append(val) return msg % tuple(data)
def _formatTransaction(self, tsInfo): """Return a string containing a human-readable formatted summary of the transaction. :param tsInfo: :class:`yum.transactioninfo.TransactionData` instance that contains information about the transaction :return: a string that contains a formatted summary of the transaction """ # Sort the packages in the transaction into different lists, # e.g. installed, updated etc tsInfo.makelists(True, True) # For each package list, pkglist_lines will contain a tuple # that contains the name of the list, and a list of tuples # with information about each package in the list pkglist_lines = [] data = {'n' : {}, 'v' : {}, 'r' : {}} a_wid = 0 # Arch can't get "that big" ... so always use the max. def _add_line(lines, data, a_wid, po, obsoletes=[]): # Create a tuple of strings that contain the name, arch, # version, repository, size, and obsoletes of the package # given in po. Then, append this tuple to lines. The # strings are formatted so that the tuple can be easily # joined together for output. (n,a,e,v,r) = po.pkgtup # Retrieve the version, repo id, and size of the package # in human-readable form evr = po.printVer() repoid = po.ui_from_repo size = self._format_number(float(po.size)) if a is None: # gpgkeys are weird a = 'noarch' lines.append((n, a, evr, repoid, size, obsoletes)) # Create a dict of field_length => number of packages, for # each field. for (d, v) in (("n",len(n)), ("v",len(evr)), ("r",len(repoid))): data[d].setdefault(v, 0) data[d][v] += 1 a_wid = max(a_wid, len(a)) return a_wid # Iterate through the different groups of packages for (action, pkglist) in [(_('Installing'), tsInfo.installed), (_('Updating'), tsInfo.updated), (_('Removing'), tsInfo.removed), (_('Reinstalling'), tsInfo.reinstalled), (_('Downgrading'), tsInfo.downgraded), (_('Installing for dependencies'), tsInfo.depinstalled), (_('Updating for dependencies'), tsInfo.depupdated), (_('Removing for dependencies'), tsInfo.depremoved)]: # Create a list to hold the tuples of strings for each package lines = [] # Append the tuple for each package to lines, and update a_wid for txmbr in pkglist: a_wid = _add_line(lines, data, a_wid, txmbr.po, txmbr.obsoletes) # Append the lines instance for this package list to pkglist_lines pkglist_lines.append((action, lines)) # # Iterate through other package lists # for (action, pkglist) in [(_('Skipped (dependency problems)'), # self.skipped_packages), # (_('Not installed'), self._not_found_i.values()), # (_('Not available'), self._not_found_a.values())]: # lines = [] # for po in pkglist: # a_wid = _add_line(lines, data, a_wid, po) # pkglist_lines.append((action, lines)) if not data['n']: return u'' else: # Change data to a list with the correct number of # columns, in the correct order data = [data['n'], {}, data['v'], data['r'], {}] # Calculate the space needed for each column columns = [1, a_wid, 1, 1, 5] columns = self._calcColumns(data, self.opts.output_width, columns, remainder_column = 2, indent=" ") (n_wid, a_wid, v_wid, r_wid, s_wid) = columns assert s_wid == 5 # out will contain the output as a list of strings, that # can be later joined together out = [u""" %s %s %s """ % ('=' * self.opts.output_width, self._fmtColumns(((_('Package'), -n_wid), (_('Arch'), -a_wid), (_('Version'), -v_wid), (_('Repository'), -r_wid), (_('Size'), s_wid)), u" "), '=' * self.opts.output_width)] # Add output for each package list in pkglist_lines for (action, lines) in pkglist_lines: #If the package list is empty, skip it if not lines: continue # Add the name of the package list totalmsg = u"%s:\n" % action # Add a line of output about an individual package for (n, a, evr, repoid, size, obsoletes) in lines: columns = ((n, -n_wid), (a, -a_wid), (evr, -v_wid), (repoid, -r_wid), (size, s_wid)) msg = self._fmtColumns(columns, u" ", u"\n") for obspo in sorted(obsoletes): appended = _(' replacing %s.%s %s\n') appended %= (obspo.name, obspo.arch, obspo.printVer()) msg = msg+appended totalmsg = totalmsg + msg # Append the line about the individual package to out out.append(totalmsg) # Add a summary of the transaction out.append(_(""" Transaction Summary %s """) % ('=' * self.opts.output_width)) summary_data = ( (_('Install'), len(tsInfo.installed), len(tsInfo.depinstalled)), (_('Upgrade'), len(tsInfo.updated), len(tsInfo.depupdated)), (_('Remove'), len(tsInfo.removed), len(tsInfo.depremoved)), (_('Reinstall'), len(tsInfo.reinstalled), 0), (_('Downgrade'), len(tsInfo.downgraded), 0), # (_('Skipped (dependency problems)'), len(self.skipped_packages), 0), # (_('Not installed'), len(self._not_found_i.values()), 0), # (_('Not available'), len(self._not_found_a.values()), 0), ) max_msg_action = 0 max_msg_count = 0 max_msg_pkgs = 0 max_msg_depcount = 0 for action, count, depcount in summary_data: if not count and not depcount: continue msg_pkgs = P_('Package', 'Packages', count) len_msg_action = utf8_width(action) len_msg_count = utf8_width(str(count)) len_msg_pkgs = utf8_width(msg_pkgs) if depcount: len_msg_depcount = utf8_width(str(depcount)) else: len_msg_depcount = 0 max_msg_action = max(len_msg_action, max_msg_action) max_msg_count = max(len_msg_count, max_msg_count) max_msg_pkgs = max(len_msg_pkgs, max_msg_pkgs) max_msg_depcount = max(len_msg_depcount, max_msg_depcount) for action, count, depcount in summary_data: msg_pkgs = P_('Package', 'Packages', count) if depcount: msg_deppkgs = P_('Dependent package', 'Dependent packages', depcount) if count: msg = '%s %*d %s (+%*d %s)\n' out.append(msg % (utf8_width_fill(action, max_msg_action), max_msg_count, count, utf8_width_fill(msg_pkgs, max_msg_pkgs), max_msg_depcount, depcount, msg_deppkgs)) else: msg = '%s %*s %s ( %*d %s)\n' out.append(msg % (utf8_width_fill(action, max_msg_action), max_msg_count, '', utf8_width_fill('', max_msg_pkgs), max_msg_depcount, depcount, msg_deppkgs)) elif count: msg = '%s %*d %s\n' out.append(msg % (utf8_width_fill(action, max_msg_action), max_msg_count, count, msg_pkgs)) return ''.join(out)