def format_name(name, indent, max_length): """ Formats a potentially long name for multi-line display, giving it a columned effect. Assumes the first line is already properly indented. """ if not name or not max_length or (max_length - indent) <= 2 or not isinstance( name, six.string_types): return name if not isinstance(name, six.text_type): name = name.decode("utf-8") words = name.split() lines = [] # handle emtpty names if not words: return name # Preserve leading whitespace in front of the first word leading_space = len(name) - len(name.lstrip()) words[0] = name[0:leading_space] + words[0] # If there is leading whitespace, we've already indented the word and don't # want to double count. current = indent - leading_space if current < 0: current = 0 def add_line(): lines.append(' '.join(line)) line = [] # Split here and build it back up by word, this way we get word wrapping while words: word = words.pop(0) if current + utf8_width(word) <= max_length: current += utf8_width( word) + 1 # Have to account for the extra space line.append(word) else: if line: add_line() # If the word will not fit, break it if indent + utf8_width(word) > max_length: split_index = 0 while (utf8_width(word[:split_index + 1]) + indent <= max_length): split_index += 1 words.insert(0, word[split_index:]) word = word[:split_index] line = [word] if indent and lines: line.insert(0, ' ' * (indent - 1)) current = indent + utf8_width(word) + 1 add_line() return '\n'.join(lines)
def format_name(name, indent, max_length): """ Formats a potentially long name for multi-line display, giving it a columned effect. Assumes the first line is already properly indented. """ if not name or not max_length or (max_length - indent) <= 2 or not isinstance(name, basestring): return name if not isinstance(name, unicode): name = name.decode("utf-8") words = name.split() lines = [] # handle emtpty names if not words: return name # Preserve leading whitespace in front of the first word leading_space = len(name) - len(name.lstrip()) words[0] = name[0:leading_space] + words[0] # If there is leading whitespace, we've already indented the word and don't # want to double count. current = indent - leading_space if current < 0: current = 0 def add_line(): lines.append(' '.join(line)) line = [] # Split here and build it back up by word, this way we get word wrapping while words: word = words.pop(0) if current + utf8_width(word) <= max_length: current += utf8_width(word) + 1 # Have to account for the extra space line.append(word) else: if line: add_line() # If the word will not fit, break it if indent + utf8_width(word) > max_length: split_index = 0 while(utf8_width(word[:split_index + 1]) + indent <= max_length): split_index += 1 words.insert(0, word[split_index:]) word = word[:split_index] line = [word] if indent and lines: line.insert(0, ' ' * (indent - 1)) current = indent + utf8_width(word) + 1 add_line() return '\n'.join(lines)
def _importplugins(self, types): '''Load plugins matching the given types. ''' # Initialise plugin dict self._plugins = {} self._pluginfuncs = {} for slot in SLOTS: self._pluginfuncs[slot] = [] # Import plugins self._used_disable_plugin = set() self._used_enable_plugin = set() for dir in self.searchpath: if not os.path.isdir(dir): continue for modulefile in sorted(glob.glob('%s/*.py' % dir)): self._loadplugin(modulefile, types) # If we are in verbose mode we get the full 'Loading "blah" plugin' lines if (self._plugins and not self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)): # Mostly copied from YumOutput._outKeyValFill() key = _("Loaded plugins: ") val = ", ".join(sorted(self._plugins)) nxt = ' ' * (utf8_width(key) - 2) + ': ' width = 80 if hasattr(self.base, 'term'): width = self.base.term.columns self.verbose_logger.log( logginglevels.INFO_2, fill(val, width=width, initial_indent=key, subsequent_indent=nxt)) if self.disabledPlugins: for wc in self.disabledPlugins: if wc not in self._used_disable_plugin: self.verbose_logger.log(logginglevels.INFO_2, _("No plugin match for: %s") % wc) del self._used_disable_plugin if self.enabledPlugins: for wc in self.enabledPlugins: if wc not in self._used_enable_plugin: self.verbose_logger.log(logginglevels.INFO_2, _("No plugin match for: %s") % wc) del self._used_enable_plugin
def _importplugins(self, types): '''Load plugins matching the given types. ''' # Initialise plugin dict self._plugins = {} self._pluginfuncs = {} for slot in SLOTS: self._pluginfuncs[slot] = [] # Import plugins self._used_disable_plugin = set() self._used_enable_plugin = set() for dir in self.searchpath: if not os.path.isdir(dir): continue for modulefile in sorted(glob.glob('%s/*.py' % dir)): self._loadplugin(modulefile, types) # If we are in verbose mode we get the full 'Loading "blah" plugin' lines if (self._plugins and not self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)): # Mostly copied from YumOutput._outKeyValFill() key = _("Loaded plugins: ") val = ", ".join(sorted(self._plugins)) nxt = ' ' * (utf8_width(key) - 2) + ': ' width = 80 if hasattr(self.base, 'term'): width = self.base.term.columns self.verbose_logger.log(logginglevels.INFO_2, fill(val, width=width, initial_indent=key, subsequent_indent=nxt)) if self.disabledPlugins: for wc in self.disabledPlugins: if wc not in self._used_disable_plugin: self.verbose_logger.log(logginglevels.INFO_2, _("No plugin match for: %s") % wc) del self._used_disable_plugin if self.enabledPlugins: for wc in self.enabledPlugins: if wc not in self._used_enable_plugin: self.verbose_logger.log(logginglevels.INFO_2, _("No plugin match for: %s") % wc) del self._used_enable_plugin
resultmsgs = [exception2msg(e)] except KeyboardInterrupt: return self.exUserCancel() except IOError, e: return self.exIOError(e) # Act on the depsolve result if result == 0: # Normal exit if self.unlock(): return 200 return 0 elif result == 1: # Fatal error for msg in resultmsgs: prefix = _('Error: %s') prefix2nd = (' ' * (utf8_width(prefix) - 2)) self.logger.critical(prefix, msg.replace('\n', '\n' + prefix2nd)) if not self.conf.skip_broken: self.verbose_logger.info(_(" You could try using --skip-broken to work around the problem")) if not self._rpmdb_warn_checks(out=self.verbose_logger.info, warn=False): self.verbose_logger.info(_(" You could try running: rpm -Va --nofiles --nodigest")) if self.unlock(): return 200 return 1 elif result == 2: # Continue on pass else: self.logger.critical(_('Unknown Error(s): Exit Code: %d:'), result) for msg in resultmsgs: self.logger.critical(msg) if self.unlock(): return 200
def ljust_wide(in_str, padding): return in_str + ' ' * (padding - utf8_width(in_str))
resultmsgs = [unicode(e)] except KeyboardInterrupt: return exUserCancel() except IOError, e: return exIOError(e) # Act on the depsolve result if result == 0: # Normal exit if unlock(): return 200 return 0 elif result == 1: # Fatal error for msg in resultmsgs: prefix = _('Error: %s') prefix2nd = (' ' * (utf8_width(prefix) - 2)) logger.critical(prefix, msg.replace('\n', '\n' + prefix2nd)) if not base.conf.skip_broken: verbose_logger.info( _(" You could try using --skip-broken to work around the problem" )) if not base._rpmdb_warn_checks(out=verbose_logger.info, warn=False): verbose_logger.info( _(" You could try running: rpm -Va --nofiles --nodigest")) if unlock(): return 200 return 1 elif result == 2: # Continue on pass else: logger.critical(_('Unknown Error(s): Exit Code: %d:'), result)
except KeyboardInterrupt: return self.exUserCancel() except IOError, e: return self.exIOError(e) # Act on the depsolve result if result == 0: # Normal exit if self.unlock(): return 200 return 0 elif result == 1: # Fatal error for msg in resultmsgs: prefix = _("Error: %s") prefix2nd = " " * (utf8_width(prefix) - 2) self.logger.critical(prefix, msg.replace("\n", "\n" + prefix2nd)) if not self.conf.skip_broken: self.verbose_logger.info(_(" You could try using --skip-broken to work around the problem")) if not self._rpmdb_warn_checks(out=self.verbose_logger.info, warn=False): self.verbose_logger.info(_(" You could try running: rpm -Va --nofiles --nodigest")) if self.unlock(): return 200 return 1 elif result == 2: # Continue on pass else: self.logger.critical(_("Unknown Error(s): Exit Code: %d:"), result) for msg in resultmsgs: self.logger.critical(msg)
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)
def _calcColumns(self, data, total_width, columns=None, remainder_column=0, indent=''): """Dynamically calculate the widths of the columns that the fields in data should be placed into for output. :param data: a list of dictionaries that represent the data to be output. Each dictionary in the list corresponds to annn column of output. The keys of the dictionary are the lengths of the items to be output, and the value associated with a key is the number of items of that length. :param total_width: the total width of the output. :param columns: a list containing the minimum amount of space that must be allocated for each row. This can be used to ensure that there is space available in a column if, for example, the actual lengths of the items being output cannot be given in *data* :param remainder_column: number of the column to receive a few extra spaces that may remain after other allocation has taken place :param indent: string that will be prefixed to a line of output to create e.g. an indent :return: a list of the widths of the columns that the fields in data should be placed into for output """ if total_width is None: total_width = self.term.columns cols = len(data) # Convert the data to ascending list of tuples, (field_length, pkgs) pdata = data data = [None] * cols # Don't modify the passed in data for d in range(0, cols): data[d] = sorted(pdata[d].items()) # We start allocating 1 char to everything but the last column, and a # space between each (again, except for the last column). Because # at worst we are better with: # |one two three| # | four | # ...than: # |one two three| # | f| # |our | # ...the later being what we get if we pre-allocate the last column, and # thus. the space, due to "three" overflowing it's column by 2 chars. if columns is None: columns = [1] * (cols - 1) columns.append(0) total_width -= (sum(columns) + (cols - 1) + utf8_width(indent)) if not columns[-1]: total_width += 1 while total_width > 0: # Find which field all the spaces left will help best helps = 0 val = 0 for d in xrange(0, cols): thelps = self._calc_columns_spaces_helps(columns[d], data[d], total_width) if not thelps: continue # We prefer to overflow: the last column, and then earlier # columns. This is so that in the best case (just overflow the # last) ... grep still "works", and then we make it prettier. if helps and (d == (cols - 1)) and (thelps / 2) < helps: continue if thelps < helps: continue helps = thelps val = d # If we found a column to expand, move up to the next level with # that column and start again with any remaining space. if helps: diff = data[val].pop(0)[0] - columns[val] if not columns[val] and (val == (cols - 1)): # If we are going from 0 => N on the last column, take 1 # for the space before the column. total_width -= 1 columns[val] += diff total_width -= diff continue overflowed_columns = 0 for d in xrange(0, cols): if not data[d]: continue overflowed_columns += 1 if overflowed_columns: # Split the remaining spaces among each overflowed column # equally norm = total_width / overflowed_columns for d in xrange(0, cols): if not data[d]: continue columns[d] += norm total_width -= norm # Split the remaining spaces among each column equally, except the # last one. And put the rest into the remainder column cols -= 1 norm = total_width / cols for d in xrange(0, cols): columns[d] += norm columns[remainder_column] += total_width - (cols * norm) total_width = 0 return columns