[yum-commits] 2 commits - yum-cron/yum-cron.py

zpavlas at osuosl.org zpavlas at osuosl.org
Fri Dec 13 15:15:21 UTC 2013


 yum-cron/yum-cron.py |  485 ++-------------------------------------------------
 1 file changed, 23 insertions(+), 462 deletions(-)

New commits:
commit 1f4b4a800516ec56215a4bed36293f62b77e1239
Author: Zdenek Pavlas <zpavlas at redhat.com>
Date:   Fri Dec 13 10:11:34 2013 +0100

    yum-cron: remove _formatTransaction()
    
    By moving the formatting out of emitters, we loose a bit of flexibility,
    but at least run the formatting only once, and don't have to pass YumBase
    references to emitters.

diff --git a/yum-cron/yum-cron.py b/yum-cron/yum-cron.py
index 5661fbb..a1fd10b 100755
--- a/yum-cron/yum-cron.py
+++ b/yum-cron/yum-cron.py
@@ -37,25 +37,23 @@ class UpdateEmitter(object):
         self.opts  = opts
         self.output = []
 
-    def updatesAvailable(self, tsInfo):
+    def updatesAvailable(self, summary):
         """Appends a message to the output list stating that there are
         updates available.
 
-        :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
-           instance that contains information about the transaction.
+        :param summary: A human-readable summary of the transaction.
         """
         self.output.append('The following updates are available on %s:' % self.opts.system_name)
-        self.output.append(self._formatTransaction(tsInfo))
+        self.output.append(summary)
 
-    def updatesDownloading(self, tsInfo):
+    def updatesDownloading(self, summary):
         """Append a message to the output list stating that
         downloading updates has started.
 
-        :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
-           instance that contains information about the transaction.
+        :param summary: A human-readable summary of the transaction.
         """
         self.output.append('The following updates will be downloaded on %s:' % self.opts.system_name)
-        self.output.append(self._formatTransaction(tsInfo))
+        self.output.append(summary)
 
     def updatesDownloaded(self):
         """Append a message to the output list stating that updates
@@ -63,15 +61,14 @@ class UpdateEmitter(object):
         """
         self.output.append("Updates downloaded successfully.")
 
-    def updatesInstalling(self, tsInfo):
+    def updatesInstalling(self, summary):
         """Append a message to the output list stating that
         installing updates has started.
 
-        :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
-           instance that contains information about the transaction.
+        :param summary: A human-readable summary of the transaction.
         """
         self.output.append('The following updates will be applied on %s:' % self.opts.system_name)
-        self.output.append(self._formatTransaction(tsInfo))
+        self.output.append(summary)
 
     def updatesInstalled(self):
         """Append a message to the output list stating that updates
@@ -156,10 +153,6 @@ class UpdateEmitter(object):
         """
         pass
 
-    def _formatTransaction(self, tsInfo):
-        assert self.opts._base.tsInfo == tsInfo
-        return self.opts._base.listTransaction()
-
 
 class EmailEmitter(UpdateEmitter):
     """Emitter class to send messages via email."""
@@ -168,14 +161,13 @@ class EmailEmitter(UpdateEmitter):
         super(EmailEmitter, self).__init__(opts)        
         self.subject = ""
 
-    def updatesAvailable(self, tsInfo):
+    def updatesAvailable(self, summary):
         """Appends a message to the output list stating that there are
         updates available, and set an appropriate subject line.
 
-        :param tsInfo: A :class:`yum.transactioninfo.TransactionData`
-           instance that contains information about the transaction.
+        :param summary: A human-readable summary of the transaction.
         """
-        super(EmailEmitter, self).updatesAvailable(tsInfo)
+        super(EmailEmitter, self).updatesAvailable(summary)
         self.subject = "Yum: Updates Available on %s" % self.opts.system_name
 
     def updatesDownloaded(self):
@@ -317,7 +309,6 @@ class YumCronBase(yum.YumBase, YumOutput):
         self.readConfigFile(config_file_name)
         self.term.reinit(color='never')
         self.term.columns = self.opts.output_width
-        self.opts._base = self
 
 
         # Create the emitters, and add them to the list
@@ -655,11 +646,13 @@ class YumCronBase(yum.YumBase, YumOutput):
 
     def emitAvailable(self):
         """Emit a notice stating whether updates are available."""
-        map(lambda x: x.updatesAvailable(self.tsInfo), self.emitters)
+        summary = self.listTransaction()
+        map(lambda x: x.updatesAvailable(summary), self.emitters)
 
     def emitDownloading(self):
         """Emit a notice stating that updates are downloading."""
-        map(lambda x: x.updatesDownloading(self.tsInfo), self.emitters)
+        summary = self.listTransaction()
+        map(lambda x: x.updatesDownloading(summary), self.emitters)
 
     def emitDownloaded(self):
         """Emit a notice stating that updates have downloaded."""
@@ -669,7 +662,8 @@ class YumCronBase(yum.YumBase, YumOutput):
         """Emit a notice stating that automatic updates are about to
         be applied.
         """
-        map(lambda x: x.updatesInstalling(self.tsInfo), self.emitters)
+        summary = self.listTransaction()
+        map(lambda x: x.updatesInstalling(summary), self.emitters)
 
     def emitInstalled(self):
         """Emit a notice stating that automatic updates have been applied."""
commit d0bba60912ec09dd6bbbc1dfbf2b27688bec3e68
Author: Zdenek Pavlas <zpavlas at redhat.com>
Date:   Fri Dec 13 09:39:36 2013 +0100

    yum-cron: Inherit YumOutput. BZ 1040109
    
    When yum-cron can use bits from output.py, we don't have to
    duplicate about 450 lines of code.

diff --git a/yum-cron/yum-cron.py b/yum-cron/yum-cron.py
index f53fbfb..5661fbb 100755
--- a/yum-cron/yum-cron.py
+++ b/yum-cron/yum-cron.py
@@ -21,6 +21,7 @@ from yum.misc import setup_locale
 
 # FIXME: is it really sane to use this from here?
 sys.path.append('/usr/share/yum-cli')
+from output import YumOutput
 import callback
 
 default_config_file = '/etc/yum/yum-cron.conf'
@@ -155,447 +156,9 @@ class UpdateEmitter(object):
         """
         pass
 
-    def _format_number(self, number, SI=0, space=' '):
-        """Return a human-readable metric-like string representation
-        of a number.
-
-        :param number: the number to be converted to a human-readable form
-        :param SI: If is 0, this function will use the convention
-           that 1 kilobyte = 1024 bytes, otherwise, the convention
-           that 1 kilobyte = 1000 bytes will be used
-        :param space: string that will be placed between the number
-           and the SI prefix
-        :return: a human-readable metric-like string representation of
-           *number*
-        """
-        symbols = [ ' ', # (none)
-                    'k', # kilo
-                    'M', # mega
-                    'G', # giga
-                    'T', # tera
-                    'P', # peta
-                    'E', # exa
-                    'Z', # zetta
-                    'Y'] # yotta
-    
-        if SI: step = 1000.0
-        else: step = 1024.0
-    
-        thresh = 999
-        depth = 0
-        max_depth = len(symbols) - 1
-    
-        # we want numbers between 0 and thresh, but don't exceed the length
-        # of our list.  In that event, the formatting will be screwed up,
-        # but it'll still show the right number.
-        while number > thresh and depth < max_depth:
-            depth  = depth + 1
-            number = number / step
-    
-        if type(number) == type(1) or type(number) == type(1L):
-            format = '%i%s%s'
-        elif number < 9.95:
-            # must use 9.95 for proper sizing.  For example, 9.99 will be
-            # rounded to 10.0 with the .1f format string (which is too long)
-            format = '%.1f%s%s'
-        else:
-            format = '%.0f%s%s'
-    
-        return(format % (float(number or 0), space, symbols[depth]))
-
-    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 _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
-
-    @staticmethod
-    def _fmt_column_align_width(width):
-        if width < 0:
-            return (u"-", -width)
-        return (u"", width)
-
-    @staticmethod
-    def _calc_columns_spaces_helps(current, data_tups, left):
-        """ Spaces left on the current field will help how many pkgs? """
-        ret = 0
-        for tup in data_tups:
-            if left < (tup[0] - current):
-                break
-            ret += tup[1]
-        return ret
-
     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
-
-        ninstalled = self.tsInfo.installed
-        ginstalled = {}
-        if self.conf.group_command == 'objects' and ninstalled:
-            # Show new pkgs. that are installed via. a group.
-            ninstalled = []
-            for txmbr in self.tsInfo.installed:
-                if not hasattr(txmbr, '_ugroup_member'):
-                    ninstalled.append(txmbr)
-                    continue
-                if txmbr._ugroup_member not in ginstalled:
-                    ginstalled[txmbr._ugroup_member] = []
-                ginstalled[txmbr._ugroup_member].append(txmbr)
-
-        for grp in sorted(ginstalled, key=lambda x: x.ui_name):
-            action = _('Installing for group upgrade "%s"') % grp.ui_name
-            pkglist = ginstalled[grp]
-
-            lines = []
-            for txmbr in pkglist:
-                a_wid = _add_line(lines, data, a_wid, txmbr.po, txmbr.obsoletes)
-
-            pkglist_lines.append((action, lines))        
-
-        # Iterate through the different groups of packages
-        for (action, pkglist) in [(_('Installing'), ninstalled),
-                            (_('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)
+        assert self.opts._base.tsInfo == tsInfo
+        return self.opts._base.listTransaction()
 
 
 class EmailEmitter(UpdateEmitter):
@@ -738,7 +301,7 @@ class YumCronConfig(BaseConfig):
     group_package_types = ListOption(['mandatory', 'default'])
 
 
-class YumCronBase(yum.YumBase):
+class YumCronBase(yum.YumBase, YumOutput):
     """Main class to check for and apply the updates."""
 
     def __init__(self, config_file_name = None):
@@ -748,9 +311,13 @@ class YumCronBase(yum.YumBase):
            config file to use.
         """
         yum.YumBase.__init__(self)
+        YumOutput.__init__(self)
 
         # Read the config file
         self.readConfigFile(config_file_name)
+        self.term.reinit(color='never')
+        self.term.columns = self.opts.output_width
+        self.opts._base = self
 
 
         # Create the emitters, and add them to the list


More information about the Yum-commits mailing list