Elliot Peele avatar Elliot Peele committed 01d8f7d

first go at removing dead code

Comments (0)

Files changed (50)

Make.defs

-#
-# Copyright (c) 2006-2008 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-VERSION=0.1
-# XVERSION can be overridden by snapshot builds to force incompatible versions
-
-export prefix = /usr
-export bindir = $(prefix)/bin
-export sbindir = $(prefix)/sbin
-export libdir = $(prefix)/lib
-export libexecdir = $(prefix)/libexec
-export datadir = $(prefix)/share
-export mandir = $(datadir)/man
-export sitedir = $(libdir)/python$(PYVERSION)/site-packages/
-export rbuilddir = $(sitedir)/rbuild
-export plugindir = $(datadir)/rbuild/plugins/
-export initdir = /etc/init.d
-export sysconfdir = /etc/sysconfig
-
-export $(VERSION)

Make.rules

-#
-# Copyright (c) 2006-2008 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-PYTHON = $(shell [ -x /usr/bin/python2.4 ] && echo /usr/bin/python2.4)
-PYVERSION = $(shell $(PYTHON) -c 'import os, sys; print sys.version[:3]')
-PYINCLUDE = $(shell $(PYTHON) -c 'import os, sys; print os.sep.join((sys.prefix, "include", "python" + sys.version[:3]))')
-
-pyfiles-install:
-	mkdir -p $(DESTDIR)$(rbuilddir)
-	for f in $(python_files); do \
-		mkdir -p `dirname $(DESTDIR)/$(sitedir)/$(DIR)/$$f`; \
-		cp -a $$f $(DESTDIR)/$(sitedir)/$(DIR)/$$f; \
-	done
-
-default-dist: dist-subdirs
-	for f in $(dist_files); do \
-		mkdir -p $(DISTDIR)/$(DIR)/`dirname $$f`; \
-		cp -a $$f $(DISTDIR)/$(DIR)/$$f; \
-	done
-
-default-install:
-
-default-all:
-
-default-clean: clean-subdirs
-	rm -f *~ .??*~ .#* *.pyo *.pyc *,cover $(generated_files) *.orig *.ccs
-
-default-test:
-	$(TESTSUITE) *.py
-
-default-subdirs:
-	for d in $(SUBDIRS); do make -C $$d DIR=$$d || exit 1; done
-
-clean-subdirs:
-ifdef SUBDIRS
-	for d in $(SUBDIRS); do make -C $$d DIR=$(DIR)/$$d clean || exit 1; done
-endif
-
-install-subdirs:
-ifdef SUBDIRS
-	for d in $(SUBDIRS); do make -C $$d DIR=$(DIR)/$$d install || exit 1; done
-endif
-
-dist-subdirs:
-ifdef SUBDIRS
-	for d in $(SUBDIRS); do make -C $$d DIR=$(DIR)/$$d dist || exit 1; done
-endif

Makefile

-#
-# Copyright (c) 2006-2008 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-all: default-subdirs default-all
-
-export TOPDIR = $(shell pwd)
-export DISTDIR = $(TOPDIR)/mirrorball-$(VERSION)
-
-SUBDIRS=updatebot repomd rpmimport pylint test
-
-extra_files = \
-	Make.rules 		\
-	Makefile		\
-	Make.defs		\
-
-dist_files = $(extra_files)
-
-.PHONY: clean dist install subdirs
-
-subdirs: default-subdirs
-
-install: install-subdirs
-
-clean: clean-subdirs default-clean
-
-dist:
-	if ! grep "^Changes in $(VERSION)" NEWS > /dev/null 2>&1; then \
-		echo "no NEWS entry"; \
-		exit 1; \
-	fi
-	$(MAKE) forcedist
-
-
-archive: $(dist_files)
-	rm -rf $(DISTDIR)
-	mkdir $(DISTDIR)
-	for d in $(SUBDIRS); do make -C $$d DIR=$$d dist || exit 1; done
-	for f in $(dist_files); do \
-		mkdir -p $(DISTDIR)/`dirname $$f`; \
-		cp -a $$f $(DISTDIR)/$$f; \
-	done; \
-	tar cjf $(DISTDIR).tar.bz2 `basename $(DISTDIR)`
-
-forcedist: archive
-
-tag:
-	hg tag -f rbuild-$(VERSION)
-
-clean: clean-subdirs default-clean
-
-include Make.rules
-include Make.defs
- 
-# vim: set sts=8 sw=8 noexpandtab :

aptmd/__init__.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Module for pasring Apt repository metadata. Also includes a handy FSM for
-parsing text files in general.
-"""
-
-import os
-
-from repomd.repository import Repository
-
-from aptmd.sources import SourcesParser
-from aptmd.packages import PackagesParser
-from aptmd.errors import UnsupportedFileError
-
-class Client(object):
-    """
-    Class for interacting with apt repositories.
-    """
-
-    def __init__(self, repoUrl):
-        self._repoUrl = repoUrl
-
-        self._repo = Repository(self._repoUrl)
-        self._packages = PackagesParser()
-        self._sources = SourcesParser()
-
-    def parse(self, path):
-        """
-        Parse repository metadata.
-        """
-
-        fh = self._repo.get(path)
-        basename = os.path.basename(path)
-        if basename.startswith('Packages'):
-            return self._packages.parse(fh, path)
-        elif basename.startswith('Sources'):
-            return self._sources.parse(fh, path)
-        else:
-            raise UnsupportedFileError(path)

aptmd/common.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Common module for apt metadata parsers to inherit from.
-"""
-
-from updatebot.lib import util
-
-from aptmd.container import Container
-from aptmd.parser import ContainerizedParser as Parser
-
-class BaseContainer(Container):
-    """
-    Base class that implements a data container for entries in apt repository
-    metadata.
-    """
-
-    _slots = ('name', 'arch', 'epoch', 'version', 'release', 'mdpath',
-        'orignalmaintainer')
-
-    def __repr__(self):
-        # Instance of 'BaseContainer' has no 'name' member
-        # pylint: disable-msg=E1101
-
-        klass = self.__class__.__name__.strip('_')
-        return '<%s(%s, %s, %s, %s, %s)>' % (klass, self.name, self.epoch,
-                                             self.version, self.release,
-                                             self.arch)
-
-    def __hash__(self):
-        # Instance of 'BaseContainer' has no 'name' member
-        # pylint: disable-msg=E1101
-
-        return hash((self.name, self.epoch, self.version, self.release,
-                     self.arch))
-
-    def __cmp__(self, other):
-        return util.packageCompare(self, other)
-
-
-class BaseParser(Parser):
-    """
-    Base parser class to be used in parsing apt metadata.
-    """
-
-    def __init__(self):
-        Parser.__init__(self)
-
-        self._containerClass = BaseContainer
-        self._mdPath = None
-
-        self._bucketState = None
-
-        self._states.update({
-            'package'               : self._package,
-            'architecture'          : self._architecture,
-            'version'               : self._version,
-            'priority'              : self._keyval,
-            'section'               : self._keyval,
-            'maintainer'            : self._keyval,
-            '_maintainer'           : self._maintainer,
-            'original-maintainer'   : self._originalmaintainer,
-            ''                      : self._linewrap,
-        })
-
-    def parse(self, fn, path):
-        """
-        Parse repository metadata.
-        """
-
-        self._mdPath = path
-        return Parser.parse(self, fn)
-
-    def _newContainer(self):
-        """
-        Create a new container object and store the finished one.
-        """
-
-        self._bucketState = None
-        if self._curObj is not None:
-            self._curObj.mdpath = self._mdPath
-        return Parser._newContainer(self)
-
-    def _package(self):
-        """
-        Parse package info.
-        """
-
-        self._newContainer()
-        self._curObj.name = self._getLine()
-
-    def _architecture(self):
-        """
-        Parse architectures.
-        """
-
-        # Attribute 'arch' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        arch = self._getLine()
-        assert arch in ('all', 'i386', 'amd64')
-        self._curObj.arch = arch
-
-    def _version(self):
-        """
-        Parse versions.
-        """
-
-        # Attribute 'release' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        debVer = self._getLine()
-
-        epoch = '0'
-        if ':' in debVer:
-            epoch = debVer.split(':')[0]
-            debVer = ':'.join(debVer.split(':')[1:])
-
-
-        if '-' in debVer:
-            sdebVer = debVer.split('-')
-            version = sdebVer[0]
-            release = '-'.join(sdebVer[1:])
-        else:
-            version = debVer
-            release = '0'
-
-        self._curObj.epoch = epoch
-        self._curObj.version = version
-        self._curObj.release = release
-
-    def _originalmaintainer(self):
-        """
-        Parse orignal maintainer info.
-        """
-
-        self._curObj.orignalmaintainer = []
-        self._bucketState = '_maintainer'
-        self._maintainer()
-
-    def _maintainer(self):
-        """
-        Parse mainter.
-        """
-
-        data = ' '.join(self._line[1:])
-        if ',' in data:
-            lst = data.split(',')
-        else:
-            lst = [ data ]
-
-        self._curObj.orignalmaintainer.extend(lst)
-
-    def _linewrap(self):
-        """
-        Handle multiline sections.
-        """
-
-        if self._bucketState is None:
-            return
-
-        state = self._getState(self._bucketState)
-        func = self._states[state]
-        func()

aptmd/container.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Base container class to be used with a ContainerizedParser.
-"""
-
-class Container(object):
-    """
-    Base container class.
-    """
-
-    _slots = ('_data', )
-
-    def __init__(self):
-        # W0212 - Access to a protected member _slots of a client class
-        # pylint: disable-msg=W0212
-
-        for cls in self.__class__.__mro__:
-            if hasattr(cls, '_slots'):
-                for item in cls._slots:
-                    setattr(self, item, None)
-
-        self._data = {}
-
-    def __eq__(self, other):
-        """
-        Check for equality.
-        """
-
-        if not isinstance(other, Container):
-            return False
-        return cmp(self, other) == 0
-
-    def set(self, key, value):
-        """
-        Set data in a local dictionary, also used for __setitem__.
-        @param key - key for dictionary
-        @type key hashable object
-        @param value - value for dictionary
-        @type value object
-        """
-
-        if key not in self._data:
-            self._data[key] = value
-
-    __setitem__ = set
-
-    def get(self, key):
-        """
-        Get information from the data dict.
-        @param key - item to retrieve from the dict
-        @type key hashable object
-        """
-
-        return self._data[key]
-
-    __getitem__ = get
-
-    def finalize(self):
-        """
-        Method to be implemented by subclasses for computing any data based
-        on parsed information.
-        """

aptmd/errors.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-AptMD Error Module.
-"""
-
-class AptMDError(Exception):
-    """
-    Base error class.
-    """
-
-class UnsupportedFileError(AptMDError):
-    """
-    Raised for files that the parser doesn't know how to handle.
-    """
-
-    def __init__(self, path):
-        AptMDError.__init__(self)
-        self.path = path
-
-    def __str__(self):
-        return 'No parser available for: %s' % self.path

aptmd/packages.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Module for parsing package metadata files.
-"""
-
-from aptmd.common import BaseContainer, BaseParser
-
-class _Package(BaseContainer):
-    """
-    Package container class.
-    """
-
-    _slots = ('source', 'sourceVersion', 'location', 'summary',
-              'description')
-
-
-class PackagesParser(BaseParser):
-    """
-    Package MD Parser class.
-    """
-
-    def __init__(self):
-        BaseParser.__init__(self)
-
-        self._containerClass = _Package
-        self._states.update({
-            'installed-size'        : self._keyval,
-            'source'                : self._source,
-            'replaces'              : self._keyval,
-            'depends'               : self._keyval,
-            'recommends'            : self._keyval,
-            'conflicts'             : self._keyval,
-            'filename'              : self._filename,
-            'size'                  : self._keyval,
-            'md5sum'                : self._keyval,
-            'sha1'                  : self._keyval,
-            'sha256'                : self._keyval,
-            'description'           : self._description,
-            '_description'          : self._descriptionbucket,
-            'bugs'                  : self._keyval,
-            'origin'                : self._keyval,
-            'task'                  : self._keyval,
-        })
-
-    def parse(self, fn, path):
-        """
-        Parse a given file or file like object line by line.
-        @param fn: filename or file like object to parse.
-        @type fn: string or file like object.
-        @param path: path to the metadata file
-        @type path: string
-        """
-
-        # Attribute 'description' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        ret = BaseParser.parse(self, fn, path)
-        # If there is any text left, collect it in the description
-        if self._text:
-            self._curObj.description = self._text
-            self._text = ''
-
-        return ret
-
-    def _source(self):
-        """
-        Parse the source line.
-        """
-
-        # Attribute 'sourceVersion' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        source = self._getLine()
-        assert source != ''
-
-        # source in the form "Source: srcName (srcVer)"
-        if len(self._line) == 3:
-            source = self._line[1]
-
-            srcVer = self._line[2].strip()
-            srcVer = srcVer.strip('(')
-            srcVer = srcVer.strip(')')
-
-            self._curObj.sourceVersion = srcVer
-
-        self._curObj.source = source
-
-    def _filename(self):
-        """
-        Parse the filename line.
-        """
-
-        # Attribute 'location' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        self._curObj.location = self._getLine()
-
-    def _description(self):
-        """
-        Parse the description line.
-        """
-
-        # Attribute 'summary' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        self._curObj.summary = self._getLine()
-        self._bucketState = '_description'
-
-    def _descriptionbucket(self):
-        """
-        Handle adding lines of a description.
-        """
-
-        prefix = '\n'
-        if self._curObj.description is None:
-            self._curObj.description = ''
-            prefix = ''
-
-        self._curObj.description += prefix
-        self._curObj.description += ' '.join(self._line)

aptmd/parser.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Base parsing module, defines all low level parser classes.
-"""
-
-import re
-
-class _QuotedLineTokenizer(object):
-    """
-    Class for breaking up a line into quoted chunks.
-    """
-
-    def __init__(self):
-        self._cur = None
-        self._list = None
-        self._singleQuotedString = False
-        self._doubleQuotedString = False
-        self._states = {' ': self._space,
-                        '\'': self._singleQuote,
-                        '"': self._doubleQuote,
-                        'other': self._add,
-                       }
-
-    def tokenize(self, line):
-        """
-        Break apart the line based on quotes.
-        @param line: line from a file.
-        @type line string
-        """
-
-        self._singleQuotedString = False
-        self._doubleQuotedString = False
-
-        self._list = [[]]
-        for char in line:
-            self._cur = char
-            if char in self._states:
-                self._states[char]()
-            else:
-                self._states['other']()
-
-        if not self._list[-1]:
-            del self._list[-1]
-
-        return [ ''.join(x) for x in self._list ]
-
-    def _space(self):
-        """
-        Handle spaces.
-        """
-
-        if not self._singleQuotedString and not self._doubleQuotedString:
-            self._list.append([])
-
-    def _singleQuote(self):
-        """
-        Handle single quotes.
-        """
-
-        self._add()
-        self._singleQuotedString = not self._singleQuotedString
-
-    def _doubleQuote(self):
-        """
-        Handle double quotes.
-        """
-
-        self._add()
-        self._doubleQuotedString = not self._doubleQuotedString
-
-    def _add(self):
-        """
-        Handle all other text.
-        """
-
-        self._list[-1].append(self._cur)
-
-
-class Parser(object):
-    """
-    Base parser class.
-    """
-
-    def __init__(self):
-        self._text = ''
-        self._line = None
-        self._curObj = None
-        self._lineTokenizer = _QuotedLineTokenizer()
-
-        self._states = {}
-
-    def parse(self, fn):
-        """
-        Parse file or file line object.
-        @param fn: name of file or file like object to parse.
-        @type fn: string or file like object.
-        """
-
-        if isinstance(fn, str):
-            fileObj = open(fn)
-        else:
-            fileObj = fn
-
-        for line in fileObj:
-            self._parseLine(line)
-
-    def _parseLine(self, cfgline):
-        """
-        Process a single line.
-        @param cfgline: single line of text config file.
-        @type cfgline: string
-        """
-
-        self._line = self._lineTokenizer.tokenize(cfgline)
-        if len(self._line) > 0:
-            state = self._getState(self._line[0])
-            if state in self._states:
-                func = self._states[state]
-                if func is None:
-                    # ignore this line
-                    return
-                func()
-                self._text = ''
-            else:
-                self._text += '\n' + ' '.join(self._line)
-        else:
-            self._text += '\n'
-
-    def _getState(self, key):
-        """
-        Translate the first word of a line to a key of the state dict. This
-        method is meant to be overridden by subclasses.
-        """
-
-        # Method could be a function
-        # pylint: disable-msg=R0201
-
-        return key
-
-    def _getLine(self):
-        """
-        Get the original line after the first word.
-        """
-
-        return ' '.join(self._line[1:]).strip()
-
-    def _getFullLine(self):
-        """
-        Get the entire line including the first word.
-        """
-
-        return ' '.join(self._line).strip()
-
-    def _checkLength(self, length, gt=False):
-        """
-        Validate the length of a line.
-        """
-
-        if gt:
-            assert(len(self._line) > length)
-        else:
-            assert(len(self._line) == length)
-
-    def _keyval(self):
-        """
-        Parse a line as a key/value pair.
-        """
-
-        key = self._getState(self._line[0])
-        value = ' '.join(self._line[1:]).strip()
-        self._curObj.set(key, value)
-
-
-class ContainerizedParser(Parser):
-    """
-    Parser for files that can be split into containers. Generates a list of
-    container objects.
-    """
-
-    def __init__(self):
-        Parser.__init__(self)
-
-        self._objects = []
-        self._containerClass = None
-        self._stateFilters = {
-        }
-        self._stateLineFilters = {
-        }
-
-    def _filter(self, fltr, state):
-        """
-        Build a state based on a filter.
-        """
-
-        self._stateFilters[re.compile(fltr)] = state
-
-    def _filterLine(self, fltr, state):
-        """
-        Build a state based on line filter.
-        """
-
-        self._stateLineFilters[re.compile(fltr)] = state
-
-    def _getState(self, key):
-        """
-        Filter states based on filter map.
-        """
-
-        key = key.strip()
-        key = key.lower()
-        if key.endswith(':'):
-            key = key[:-1]
-
-        if key in self._states:
-            return key
-
-        for fltr, state in self._stateFilters.iteritems():
-            if fltr.match(key):
-                return state
-
-        for fltr, state in self._stateLineFilters.iteritems():
-            if fltr.match(self._getFullLine()):
-                return state
-
-        return key
-
-    def _newContainer(self):
-        """
-        Create a new container object and store the current one.
-        """
-
-        # self._containerClass is not callable
-        # pylint: disable-msg=E1102
-
-        if self._curObj is not None:
-            if hasattr(self._curObj, 'finalize'):
-                self._curObj.finalize()
-            self._objects.append(self._curObj)
-        self._curObj = self._containerClass()
-
-    def parse(self, fileObj):
-        """
-        Parse a file or file line object.
-        """
-
-        self._objects = []
-        Parser.parse(self, fileObj)
-        return self._objects

aptmd/sources.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Module for parsing Sources file from an Apt repository.
-"""
-
-import os
-
-from aptmd.common import BaseContainer, BaseParser
-
-class _SourcePackage(BaseContainer):
-    """
-    Container for source data.
-    """
-
-    _slots = ('binaries', 'directory', 'files', 'checksumssha1',
-        'checksumssha256')
-
-
-class SourcesParser(BaseParser):
-    """
-    Class for parsing Source metadata.
-    """
-
-    def __init__(self):
-        BaseParser.__init__(self)
-
-        self._containerClass = _SourcePackage
-        self._states.update({
-            'binary'                : self._binary,
-            'build-depends'         : self._keyval,
-            'standards-version'     : self._keyval,
-            'format'                : self._keyval,
-            'directory'             : self._directory,
-            'files'                 : self._files,
-            '_file'                 : self._file,
-            'homepage'              : self._keyval,
-            'uploaders'             : self._keyval,
-            'checksums-sha1'        : self._sha1,
-            '_checksums-sha1'       : self._sha1bucket,
-            'checksums-sha256'      : self._sha256,
-            '_checksums-sha256'     : self._sha256bucket,
-        })
-
-    def _architecture(self):
-        """
-        Parse architecture.
-        """
-
-        # Attribute 'arch' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        self._curObj.arch = 'src'
-
-    def _binary(self):
-        """
-        Parse binary info.
-        """
-
-        # Attribute 'binary' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        self._line[-1] = self._line[-1].strip()
-        self._curObj.binaries = [ x.strip(',') for x in self._line[1:] ]
-
-    def _directory(self):
-        """
-        Parse directory info.
-        """
-
-        # Attribute 'directory' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        self._curObj.directory = self._getLine()
-
-    def _files(self):
-        """
-        Parse files info.
-        """
-
-        # Attribute 'files' defined outside __init__
-        # pylint: disable-msg=W0201
-
-        self._curObj.files = []
-        self._bucketState = '_file'
-
-    def _file(self):
-        """
-        Parse file info.
-        """
-
-        if len(self._line) != 4:
-            return
-
-        fileName = self._line[3].strip()
-        path = os.path.join(self._curObj.directory, fileName)
-        self._curObj.files.append(path)
-
-    def _sha1(self):
-        """
-        Start of sha1 section.
-        """
-
-        self._curObj.checksumssha1 = []
-        self._bucketState = '_checksums-sha1'
-
-    def _sha1bucket(self):
-        """
-        Handle lines of a sha1 section.
-        """
-
-        self._curObj.checksumssha1.append(' '.join(self._line[1:]))
-
-    def _sha256(self):
-        """
-        Start of sha256 section.
-        """
-
-        self._curObj.checksumssha256 = []
-        self._bucketState = '_checksums-sha256'
-
-    def _sha256bucket(self):
-        """
-        Handle lines of a sha256 section.
-        """
-
-        self._curObj.checksumssha256.append(' '.join(self._line[1:]))

errata/__init__.py

-#
-# Copyright (c) 2010 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#

errata/centos.py

-#
-# Copyright (c) 2010 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Generate update information based on the ordering of a pkgSource.
-"""
-
-import time
-import logging
-
-from updatebot.lib import util
-
-from errata import common
-from errata.common import Nevra
-from errata.common import Package
-from errata.common import Channel
-from errata.common import Advisory
-
-log = logging.getLogger('errata')
-
-class AdvisoryManager(common.AdvisoryManager):
-    def __init__(self, pkgSource):
-        common.AdvisoryManager.__init__(self)
-
-        self._pkgSource = pkgSource
-
-        self._channels = {}
-        self._advOrder = {}
-        self._advisories = set()
-
-    @common.reqfetch
-    def iterByIssueDate(self):
-        """
-        Yields Errata objects by the issue date of the errata.
-        """
-
-        for updateId in sorted(self._advOrder):
-            for adv in self._advOrder[updateId]:
-                yield adv
-
-    def fetch(self):
-        """
-        Retrieve all required advisory data.
-
-        This is probably going to cache any data, probably in a database, that
-        is being fetched from the internet somewhere so that we don't cause
-        excesive load for anyone's servers.
-        """
-
-        self._order()
-        self._fetched = True
-        return self._advisories
-
-    @common.reqfetch
-    def getChannels(self):
-        """
-        Get a list of indexed channel names.
-        @return list of indexed channel names
-        """
-
-        return self._channels.keys()
-
-    def cleanup(self):
-        """
-        Free all cached results.
-        """
-
-        self._channels = {}
-        self._advOrder = {}
-        self._advisories = set()
-
-    def getModifiedErrata(self, updateId):
-        """
-        Get a list of any errata that were modified after updateId and were
-        issued before updateId.
-        """
-
-        return []
-
-    def _order(self):
-        """
-        Fetch all patch data from the package source.
-        """
-
-        def slice(ts):
-            """
-            Convert a time stamp into the desired time slice.
-            """
-
-            # convert to current day
-            return int(time.mktime(time.strptime(time.strftime('%Y%m%d',
-                        time.gmtime(ts)), '%Y%m%d')))
-
-        def getChannel(pkg):
-            for label, channel in self._channels.iteritems():
-                if label in pkg.location:
-                    return channel
-
-        # make sure the pkg source is loaded.
-        self._pkgSource.load()
-
-        # Sort packages by build timestamp.
-        slices = {}
-        for srcPkg in self._pkgSource.srcPkgMap:
-            if int(srcPkg.buildTimestamp) < 1286870401 or not srcPkg.fileTimestamp:
-                updateId = slice(int(srcPkg.buildTimestamp))
-            else:
-                updateId = slice(int(srcPkg.fileTimestamp))
-            # If package comes from a base path, override updateId
-            for basePath in self._pkgSource._cfg.repositoryBasePaths:
-                if basePath[1].match(srcPkg.location) is not None:
-                    updateId = 0
-                    break
-            slices.setdefault(updateId, set()).add(srcPkg)
-
-        # find labels
-        for label in self._pkgSource._clients:
-            self._channels[label] = Channel(label)
-
-        # make package objects from binaries
-        nevras = {}
-        packages = {}
-        srcPkgMap = {}
-
-        seen = set()
-        startedUpdates = False
-        for sliceId, srcPkgs in sorted(slices.iteritems()):
-            for srcPkg in srcPkgs:
-                # Assume everything before we see a dupllicate source name is in
-                # the base set of packages.
-                if srcPkg.name not in seen and not startedUpdates:
-                    seen.add(srcPkg.name)
-                    continue
-                startedUpdates = True
-
-                pkgSet = srcPkgMap.setdefault(srcPkg, set())
-                for binPkg in self._pkgSource.srcPkgMap[srcPkg]:
-                    if binPkg.arch == 'src':
-                        continue
-
-                    # Get a nevra object
-                    nevra = binPkg.getNevra()
-                    nevraObj = nevras.setdefault(nevra, Nevra(*nevra))
-
-                    # Get a channel object
-                    channelObj = getChannel(binPkg)
-
-                    # Create a package object
-                    package = Package(channelObj, nevraObj)
-                    packageObj = packages.setdefault(package, package)
-
-                    # Add packages to the package map
-                    srcPkgMap.setdefault(srcPkg, set()).add(packageObj)
-
-        # create advisories
-        for sliceId, srcPkgs in slices.iteritems():
-            for srcPkg in srcPkgs:
-                # If this source is not in the srcPkgMap it is probably
-                # considered to be in the base set of packages.
-                if srcPkg not in srcPkgMap:
-                    continue
-
-                # Collect everything needed to make an advisory.
-                advisory = 'cu-%s' % srcPkg
-                synopsis = 'update of %s' % srcPkg
-                issue_date = time.strftime('%Y-%m-%d %H:%M:%S',
-                    time.gmtime(sliceId))
-                packages = srcPkgMap[srcPkg]
-
-                # Create a fake advisory.
-                log.info('creating advisory: %s' % advisory)
-                adv = Advisory(advisory, synopsis, issue_date, packages)
-                self._advisories.add(adv)
-                self._advOrder.setdefault(sliceId, set()).add(adv)

errata/common.py

-#
-# Copyright (c) 2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-This module is here as an example of the data model and interfaces that
-mirrorball is looking for when importing and updating platforms in advisory
-order. The variables and methods that are defined below must be available.
-"""
-
-def reqfetch(func):
-    """
-    Decorator to make sure manager data is loaded before a method is called.
-    """
-
-    def wrap(self, *args, **kwargs):
-        if not self._fetched:
-            self.fetch()
-        return func(self, *args, **kwargs)
-    return wrap
-
-
-class Nevra(object):
-    """
-    Class to represent a package nevra.
-    """
-
-    __slots__ = ('name', 'epoch', 'version', 'release', 'arch', )
-
-    def __init__(self, name, epoch, version, release, arch):
-        self.name = name
-        self.epoch = epoch
-        self.version = version
-        self.release = release
-        self.arch = arch
-
-    def getNevra(self):
-        """
-        Return a tuple representation of the nevra.
-        """
-
-        return (self.name, self.epoch, self.version, self.release, self.arch)
-
-
-class Package(object):
-    """
-    Class to represent a package.
-
-    @param channels: List of channel objects that this package can be found in.
-    @type channels: list(Repository, ...)
-    """
-
-    __slots__ = ('channel', 'nevra', )
-
-    def __init__(self, channel, nevra):
-        assert isinstance(channel, Channel)
-        assert isinstance(nevra, Nevra)
-
-        self.channel = channel
-        self.nevra = nevra
-
-    def getNevra(self):
-        """
-        Returns a tuple of (name, epoch, version, release, arch) for
-        this package.
-        """
-
-        return self.nevra.getNevra()
-
-
-class Channel(object):
-    """
-    Class to represent a repository.
-
-    @param label: Unique key for the name of a repository.
-    @type label: str
-    """
-
-    __slots__ = ('label', )
-
-    def __init__(self, label):
-        self.label = label
-
-
-class Advisory(object):
-    """
-    Class to represent an errata or advisory.
-
-    @param issue_date: Date the advisory was issued in the following format.
-                       (format characters are defined in the python time
-                        module). '%Y-%m-%d %H:%M:%S'
-    @type issue_date: str
-    @param packages: List of package objects.
-    @type packages: list(Package, ...)
-    @param advisory: Unique key for the advisory
-    @type advisory: str
-    @param synopsis: Brief description of the advisory.
-    @type synopsis: str
-    """
-
-    __slots__ = ('advisory', 'synopsis', 'issue_date', 'nevraChannels', )
-
-    def __repr__(self):
-        return self.advisory
-
-    def __init__(self, advisory, synopsis, issue_date, packages):
-        self.advisory = advisory
-        self.synopsis = synopsis
-        self.issue_date = issue_date
-
-        for pkg in packages:
-            assert isinstance(pkg, Package)
-
-        self.nevraChannels = packages
-
-    def __hash__(self):
-        return hash((self.advisory, self.synopsis, self.issue_date))
-
-    def __cmp__(self, other):
-        return cmp((self.issue_date, self.advisory, self.synopsis),
-                   (other.issue_date, other.advisory, other.synopsis))
-
-
-class AdvisoryManager(object):
-    """
-    Class to provide an interface for accessing advisory information for a
-    platform that can then be matched up to a package source.
-    """
-
-    def __init__(self):
-        self._fetched = False
-
-    def getRepositories(self):
-        """
-        Returns a list of repository labels that have been fetched.
-        """
-
-        raise NotImplementedError
-
-    def iterByIssueDate(self):
-        """
-        Yields Errata objects by the issue date of the errata.
-        """
-
-        raise NotImplementedError
-
-    def fetch(self):
-        """
-        Retrieve all required advisory data.
-
-        This is probably going to cache any data, probably in a database, that
-        is being fetched from the internet somewhere so that we don't cause
-        excesive load for anyone's servers.
-        """
-
-        raise NotImplementedError
-
-    def getChannels(self):
-        """
-        Return a list of all indexed channels, will trigger a fetch if needed.
-        """
-
-        raise NotImplementedError
-
-    def cleanup(self):
-        """
-        Frees any cached results.
-        """
-
-        raise NotImplementedError
-
-    def getModifiedErrata(self, updateId):
-        """
-        Get a list of any errata that were modified after updateId and were
-        issued before updateId.
-        """
-
-        raise NotImplementedError

errata/sles.py

-#
-# Copyright (c) 2010 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Generate update information based on the patch detail in SuSE repositories.
-"""
-
-import time
-import logging
-
-from errata import common
-from errata.common import Nevra
-from errata.common import Package
-from errata.common import Channel
-from errata.common import Advisory
-
-log = logging.getLogger('errata')
-
-class AdvisoryManager(common.AdvisoryManager):
-    def __init__(self, pkgSource):
-        common.AdvisoryManager.__init__(self)
-
-        self._pkgSource = pkgSource
-
-        self._channels = {}
-        self._advOrder = {}
-        self._advisories = set()
-
-    @common.reqfetch
-    def iterByIssueDate(self):
-        """
-        Yields Errata objects by the issue date of the errata.
-        """
-
-        # FIXME: this work here? (cribbed from centos.py)
-        for updateId in sorted(self._advOrder):
-            for adv in self._advOrder[updateId]:
-                yield adv
-
-    def fetch(self):
-        """
-        Retrieve all required advisory data.
-
-        This is probably going to cache any data, probably in a database, that
-        is being fetched from the internet somewhere so that we don't cause
-        excesive load for anyone's servers.
-        """
-
-        self._order()
-        self._fetched = True
-        return self._advisories
-
-    @common.reqfetch
-    def getChannels(self):
-        """
-        Get a list of indexed channel names.
-        @return list of indexed channel names
-        """
-
-        return self._channels.keys()
-
-    def cleanup(self):
-        """
-        Free all cached results.
-        """
-
-        self._channels = {}
-        self._advOrder = {}
-        self._advisories = set()
-
-    def getModifiedErrata(self, updateId):
-        """
-        Get a list of any errata that were modified after updateId and were
-        issued before updateId.
-        """
-
-        return []
-
-    def _order(self):
-        """
-        Fetch all patch data from the package source.
-        """
-
-        def bin_timestamp(ts):
-            """
-            Convert a time stamp into the desired time slice.
-            """
-
-            # convert to current day
-            return int(time.mktime(time.strptime(time.strftime('%Y%m%d',
-                        time.gmtime(ts)), '%Y%m%d')))
-
-        def slice(patches):
-            """
-            Build a dictionary of binned (sliced) timestamps and patches.
-            """
-
-            slices = {}
-
-            for patch in patches:
-                # Bin by day:
-                updateId = bin_timestamp(int(patch.timestamp))
-                # ...or uncomment the below line to disable binning:
-                #updateId = int(patch.timestamp)
-                slices.setdefault(updateId,
-                                  set()).add(patch.getAttribute('patchid'))
-            return slices
-
-        def patchidNoRepo(patchid):
-            """
-            Trims the leading (repository) information from a patchid.
-            Used for folding patches across repositories, among other things.
-            """
-            return '-'.join(patchid.split('-')[1:])
-
-        def map_patchids(slices):
-            """
-            Build a dictionary of (partial) patchids and their
-            corresponding timestamps.  Used to determine if a patchid
-            across multiple repositories also has multiple timestamps.
-            """
-
-            patchidMap = {}
-
-            for timestamp, patchids in slices.iteritems():
-                for patchid in patchids:
-                    # Map excludes leading segment of patchid, which
-                    # carries repository information; we're folding
-                    # across repositories.
-                    patchidMap.setdefault(patchidNoRepo(patchid),
-                                          set()).add(timestamp)
-            return patchidMap
-
-        def getChannel(pkg):
-            for label, channel in self._channels.iteritems():
-                if label in pkg.location:
-                    return channel
-            raise RuntimeError , 'unable to find channel for %s' % pkg.location
-
-        def getSrcPkg(binPkg):
-            for srcPkg, binPkgs in self._pkgSource.srcPkgMap.iteritems():
-                if binPkg in binPkgs:
-                    return srcPkg
-            raise RuntimeError , 'unable to find source package for %s' % binPkg.location
-
-        def getPatchById(patches, patchid):
-            for patch in patches:
-                if patch.getAttribute('patchid') == patchid:
-                    return patch
-            raise RuntimeError , 'unable to find patch %s' % patchid
-
-        # make sure the pkg source is loaded.
-        self._pkgSource.load()
-
-        # Each client value is a SLES release/update repository object.
-        # self._pkgSource._clients.values()[...]
-
-        # now get the patch data...
-        patches = set()
-
-        for path, client in self._pkgSource.getClients().iteritems():
-            log.info('loading patches for path %s' % path)
-            for patch in client.getPatchDetail():
-                for pkg in patch.packages:
-                    pkg.location = path + '/' + pkg.location
-                patches.add(patch)
-
-        for label in self._pkgSource._clients:
-            self._channels[label] = Channel(label)
-
-        # ...and (time-)slice it up.
-        slices = slice(patches)
-
-        for timeslice, patchSet in slices.iteritems():
-            for patchId in patchSet:
-                patchObj = getPatchById(patches, patchId)
-                if patchObj.timestamp != timeslice:
-                    log.info('syncing %s timestamp (%s) to slice timestamp %s' % (
-                    patchId, patchObj.timestamp, timeslice))
-                    patchObj.timestamp = timeslice
-
-        # slices dict is still current since the above only synced the
-        # patch timestamps to existing slices.
-
-        # This maps patchid (without regard to repos) to timeslices.
-        patchidMap = map_patchids(slices)
-
-        # This requires no more than two timestamps per patchid;
-        # one each for slesp3 and sdkp3 (bails out otherwise):
-        #
-        # Pondering how this can be consolidated with above code to
-        # reduce iterating...
-        #
-        # Just so it's clear, I hate this code (i.e. FIXME).
-        #
-        for patchid, timestamps in patchidMap.iteritems():
-            if len(timestamps) > 1:
-                # Untested beyond 2.
-                assert(len(timestamps) == 2)
-                # FIXME: refactor this monster.
-                splitpatch = [ (patch.getAttribute('patchid'),
-                                set(patch.packages), patch) for patch in
-                               patches if patchidNoRepo(patch.getAttribute('patchid')) == patchid ]
-                if splitpatch[0][1].issubset(splitpatch[1][1]):
-                    log.info('syncing timestamps (%s %s) ' % (
-                        splitpatch[0][2].timestamp,
-                        splitpatch[1][2].timestamp) +
-                             'across repositories for %s & %s ' % (
-                        splitpatch[0][0], splitpatch[1][0]) +
-                             'to superset timestamp %s' % splitpatch[1][2].timestamp)
-                    splitpatch[0][2].timestamp = splitpatch[1][2].timestamp
-                elif splitpatch[1][1].issubset(splitpatch[0][1]):
-                    log.info('syncing timestamps (%s %s) ' % (
-                        splitpatch[0][2].timestamp,
-                        splitpatch[1][2].timestamp) +
-                             'across repositories for %s & %s ' % (
-                        splitpatch[0][0], splitpatch[1][0]) +
-                             'to superset timestamp %s' % splitpatch[0][2].timestamp)
-                    splitpatch[1][2].timestamp = splitpatch[0][2].timestamp
-                # So far this has only been tested in pure-subset cases.
-                else:
-                    maxtime = max(splitpatch[1][2].timestamp,
-                                  splitpatch[0][2].timestamp)
-                    log.info('neither %s nor %s is a subset of the other, syncing timestamps (%s & %s) to later timestamp: %s' % (splitpatch[0][0], splitpatch[1][0], splitpatch[1][2].timestamp, splitpatch[0][2].timestamp, maxtime))
-                    splitpatch[1][2].timestamp = splitpatch[0][2].timestamp = maxtime
-
-        advPkgMap = {}
-        nevras = {}
-        packages = {}
-        srcPkgAdvMap = {}
-        srcPkgPatchidMap = {}
-
-        for patch in patches:
-            advisory = patch.getAttribute('patchid')
-            patchid = patchidNoRepo(advisory)
-
-            for binPkg in patch.packages:
-                nevra = binPkg.getNevra()
-                nevraObj = nevras.setdefault(nevra, Nevra(*nevra))
-                channelObj = getChannel(binPkg)
-                package = Package(channelObj, nevraObj)
-                packageObj = packages.setdefault(package, package)
-                advPkgMap.setdefault(advisory, set()).add(packageObj)
-                srcPkgObj = getSrcPkg(binPkg)
-                srcPkgAdvMap.setdefault(srcPkgObj, set()).add(advisory)
-                srcPkgPatchidMap.setdefault(srcPkgObj, set()).add(patchid)
-
-                # FIXME: I hate this code too.
-                if srcPkgPatchidMap[srcPkgObj] != set([patchid]):
-                    # Untested beyond two, and expected case is two
-                    # different advisories issued for the same source
-                    # package, one each for x86 and x86_64.  (Lots of
-                    # these for the kernel, for instance.)
-                    assert(len(srcPkgPatchidMap[srcPkgObj]) == 2)
-                    srcPkgAdvs = [ getPatchById(patches, srcPkgAdv)
-                                   for srcPkgAdv in srcPkgAdvMap[srcPkgObj] ]
-                    syncTimestamp = min([ x.timestamp for x in srcPkgAdvs ])
-                    for srcPkgAdv in srcPkgAdvs:
-                        if srcPkgAdv.timestamp != syncTimestamp:
-                            log.info('syncing timestamp (%s) of %s to %s ' % (
-                                srcPkgAdv.timestamp, srcPkgAdv.getAttribute('patchid'), syncTimestamp) +
-                                     'across same-SRPM advisories for %s' % (
-                                srcPkgAdvs))
-                            srcPkgAdv.timestamp = syncTimestamp
-
-            # There should be no srcPkgs with more than two patchids.
-            assert(len([ x for x, y in srcPkgPatchidMap.iteritems()
-                         if len(y) > 2 ]) == 0)
-
-        # Now that all timestamps have been munged, make second pass to
-        # establish order & create advisories.
-        for patch in patches:
-            advisory = patch.getAttribute('patchid')
-            patchid = patchidNoRepo(advisory)
-
-            issue_date = time.strftime('%Y-%m-%d %H:%M:%S',
-                                       time.gmtime(int(patch.timestamp)))
-            log.info('creating advisory: %s (%s)' % (advisory,
-                                                     patch.timestamp))
-            adv = Advisory(advisory, patch.summary, issue_date,
-                           advPkgMap[advisory])
-            self._advisories.add(adv)
-            self._advOrder.setdefault(int(patch.timestamp), set()).add(adv)
-
-#        import epdb ; epdb.st()

errata/sles11.py

-#
-# Copyright (c) 2010 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Generate update information based on the patch detail in SuSE repositories
-for SLES11.
-"""
-
-import time
-import logging
-
-from errata.common import Nevra
-from errata.common import Package
-from errata.common import Channel
-from errata.common import Advisory
-from errata.sles import AdvisoryManager
-
-log = logging.getLogger('errata')
-
-class AdvisoryManager11(AdvisoryManager):
-    def _order(self):
-        """
-        Fetch all patch data from the package source.
-        """
-
-        def bin_timestamp(ts):
-            """
-            Convert a time stamp into the desired time slice.
-            """
-
-            # convert to current day
-            return int(time.mktime(time.strptime(time.strftime('%Y%m%d',
-                        time.gmtime(ts)), '%Y%m%d')))
-
-        def slice(patches):
-            """
-            Build a dictionary of binned (sliced) timestamps and patches.
-            """
-
-            slices = {}
-
-            for patch in patches:
-                # Bin by day:
-                updateId = bin_timestamp(int(patch.issued))
-                # ...or uncomment the below line to disable binning:
-                #updateId = int(patch.issued)
-                slices.setdefault(updateId,
-                                  set()).add(patch.id)
-            return slices
-
-        def patchidNoRepo(patchid):
-            """
-            Trims the leading (repository) information from a patchid.
-            Used for folding patches across repositories, among other things.
-            """
-            return '-'.join(patchid.split('-')[1:])
-
-        def map_patchids(slices):
-            """
-            Build a dictionary of (partial) patchids and their
-            corresponding timestamps.  Used to determine if a patchid
-            across multiple repositories also has multiple timestamps.
-            """
-
-            patchidMap = {}
-
-            for timestamp, patchids in slices.iteritems():
-                for patchid in patchids:
-                    # Map excludes leading segment of patchid, which
-                    # carries repository information; we're folding
-                    # across repositories.
-                    patchidMap.setdefault(patchidNoRepo(patchid),
-                                          set()).add(timestamp)
-            return patchidMap
-
-        def getChannel(pkg):
-            for label, channel in self._channels.iteritems():
-                if label in pkg.location:
-                    return channel
-            raise RuntimeError , 'unable to find channel for %s' % pkg.location
-
-        def getSrcPkg(binPkg, binMap):
-            srcPkg = binMap[binPkg.getDegenerateNevra()]
-            
-            if srcPkg:
-                return srcPkg
-            raise RuntimeError , 'unable to find source package for %s' % binPkg.location
-
-        def getPatchById(patches, patchid):
-            for patch in patches:
-                if patch.id == patchid:
-                    return patch
-            raise RuntimeError , 'unable to find patch %s' % patchid
-
-        # make sure the pkg source is loaded.
-        self._pkgSource.load()
-
-        # Each client value is a SLES release/update repository object.
-        # self._pkgSource._clients.values()[...]
-		
-        
-		# now get the patch data...
-        patches = set()
-
-        for path, client in self._pkgSource.getClients().iteritems():
-            log.info('loading patches for path %s' % path)
-            #for patch in client.getPatchDetail():
-            for patch in client.getUpdateInfo():
-                for pkg in patch.pkglist:
-                    pkg.location = path + '/' + pkg.filename
-                # Schema change with SLES11:
-                patches.add(patch)
-
-        for label in self._pkgSource._clients:
-            self._channels[label] = Channel(label)
-
-        # ...and (time-)slice it up.
-        slices = slice(patches)
-
-        for timeslice, patchSet in slices.iteritems():
-            for patchId in patchSet:
-                patchObj = getPatchById(patches, patchId)
-                if patchObj.issued != timeslice:
-                    log.info('syncing %s timestamp (%s) to slice timestamp %s' % (
-                    patchId, patchObj.issued, timeslice))
-                    patchObj.issued = timeslice
-
-        # slices dict is still current since the above only synced the
-        # patch timestamps to existing slices.
-
-        # This maps patchid (without regard to repos) to timeslices.
-        patchidMap = map_patchids(slices)
-        
-        # This requires no more than two timestamps per patchid;
-        # one each for slessp1 and sdksp1 (bails out otherwise):
-        #
-        # Pondering how this can be consolidated with above code to
-        # reduce iterating...
-        #
-        # Just so it's clear, I hate this code (i.e. FIXME).
-        #
-        for patchid, timestamps in patchidMap.iteritems():
-            if len(timestamps) > 1:
-                # Untested beyond 2.
-                assert(len(timestamps) == 2)
-                # FIXME: refactor this monster.
-                splitpatch = [ (patch.id,
-                                set([x.filename for x in patch.pkglist]),
-                                patch) for patch in
-                               patches if patchidNoRepo(patch.id) == patchid ]
-                if splitpatch[0][1].issubset(splitpatch[1][1]):
-                    log.info('syncing timestamps (%s %s) ' % (
-                        splitpatch[0][2].issued,
-                        splitpatch[1][2].issued) +
-                             'across repositories for %s & %s ' % (
-                        splitpatch[0][0], splitpatch[1][0]) +
-                             'to superset timestamp %s' % splitpatch[1][2].issued)
-                    splitpatch[0][2].issued = splitpatch[1][2].issued
-                elif splitpatch[1][1].issubset(splitpatch[0][1]):
-                    log.info('syncing timestamps (%s %s) ' % (
-                        splitpatch[0][2].issued,
-                        splitpatch[1][2].issued) +
-                             'across repositories for %s & %s ' % (
-                        splitpatch[0][0], splitpatch[1][0]) +
-                             'to superset timestamp %s' % splitpatch[0][2].issued)
-                    splitpatch[1][2].issued = splitpatch[0][2].issued
-                else:
-                    maxtime = max(splitpatch[1][2].issued,
-                                  splitpatch[0][2].issued)
-                    log.info('neither %s nor %s is a subset of the other, syncing timestamps (%s & %s) to later timestamp: %s' % (splitpatch[0][0], splitpatch[1][0], splitpatch[1][2].issued, splitpatch[0][2].issued, maxtime))
-                    splitpatch[1][2].issued = splitpatch[0][2].issued = maxtime
-
-        advPkgMap = {}
-        nevras = {}
-        packages = {}
-        srcPkgAdvMap = {}
-        srcPkgPatchidMap = {}
-
-        binMap = dict([ (x.getNevra(), y) for x,y in
-                        self._pkgSource.binPkgMap.iteritems() ])
-
-        for patch in patches:
-            advisory = patch.id
-            patchid = patch.id
-
-            for binPkg in patch.pkglist:
-                nevra = binPkg.getNevra()
-                nevraObj = nevras.setdefault(nevra, Nevra(*nevra))
-                channelObj = getChannel(binPkg)
-                package = Package(channelObj, nevraObj)
-                packageObj = packages.setdefault(package, package)
-                advPkgMap.setdefault(advisory, set()).add(packageObj)
-                srcPkgObj = getSrcPkg(binPkg, binMap)
-                srcPkgAdvMap.setdefault(srcPkgObj, set()).add(advisory)
-                srcPkgPatchidMap.setdefault(srcPkgObj, set()).add(patchid)
-
-                # FIXME: I hate this code too.
-                # Note that to date this has not been tested in anger
-                # on SLES11, as no advisory releases have spanned
-                # multiple buckets.
-                if srcPkgPatchidMap[srcPkgObj] != set([patchid]):
-                    # Untested beyond two, and expected case is two
-                    # different advisories issued for the same source
-                    # package, one each for x86 and x86_64.  (Lots of
-                    # these for the kernel, for instance.)
-                    assert(len(srcPkgPatchidMap[srcPkgObj]) == 2)
-                    srcPkgAdvs = [ getPatchById(patches, srcPkgAdv)
-                                   for srcPkgAdv in srcPkgAdvMap[srcPkgObj] ]
-                    syncTimestamp = min([ x.issued for x in srcPkgAdvs ])
-                    for srcPkgAdv in srcPkgAdvs:
-                        if srcPkgAdv.issued != syncTimestamp:
-                            log.info('syncing timestamp (%s) of %s to %s ' % (
-                                srcPkgAdv.issued, srcPkgAdv.id, syncTimestamp) +
-                                     'across same-SRPM advisories for %s' % (
-                                srcPkgAdvs))
-                            srcPkgAdv.issued = syncTimestamp
-                #import epdb ; epdb.st()
-
-            # There should be no srcPkgs with more than two patchids.
-            assert(len([ x for x, y in srcPkgPatchidMap.iteritems()
-                         if len(y) > 2 ]) == 0)
-
-        # Now that all timestamps have been munged, make second pass to
-        # establish order & create advisories. 
-        for patch in patches:
-            advisory = patch.id
-            
-            issue_date = time.strftime('%Y-%m-%d %H:%M:%S',
-                                       time.gmtime(int(patch.issued)))
-            log.info('creating advisory: %s (%s)' % (advisory,
-                                                     patch.issued))
-            adv = Advisory(advisory, patch.summary, issue_date,
-                           advPkgMap[advisory])
-            self._advisories.add(adv)
-            self._advOrder.setdefault(int(patch.issued), set()).add(adv)
-
-#        import epdb ; epdb.st()

pmap/__init__.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-PMAP - PiperMail Archive Parser
-
-Parse advisories from pipermail archives.
-"""
-
-import os
-import gzip
-import shutil
-import urllib2
-import tempfile
-
-from imputil import imp
-
-__all__ = ('InvalidBackendError', 'ArchiveNotFoundError', 'parse')
-__supportedBackends = ('ubuntu', 'centos', 'scientific')
-
-class InvalidBackendError(Exception):
-    """
-    Raised when requested backend is not available.
-    """
-
-class ArchiveNotFoundError(Exception):
-    """
-    Raised when an archive could not be retrieved.
-    """
-
-def _getFileObjFromUrl(url):
-    """
-    Given a URL download the file, gunzip if needed, and return an open
-    file object.
-    """
-
-    fn = tempfile.mktemp(prefix='pmap')
-
-    # download file
-    try:
-        inf = urllib2.urlopen(url)
-    except urllib2.HTTPError, e:
-        if e.getcode() == 404:
-            raise ArchiveNotFoundError, e
-    outf = open(fn, 'w')
-    shutil.copyfileobj(inf, outf)
-
-    if url.endswith('.gz'):
-        fh = gzip.open(fn)
-    else:
-        fh = open(fn)
-
-    os.unlink(fn)
-    return fh
-
-def _getBackend(backend):
-    """
-    If the requested backend exists find it and return the backend module,
-    otherwise raise an exception.
-    """
-
-    if backend not in __supportedBackends:
-        raise InvalidBackendError('%s is not a supported backend, please '
-            'choose from %s' % (backend, ','.join(__supportedBackends)))
-
-    try:
-        path = [imp.find_module('pmap')[1], ]
-        mod = imp.find_module(backend, path)
-        loaded = imp.load_module(backend, mod[0], mod[1], mod[2])
-        return loaded
-    except ImportError, e:
-        raise InvalidBackendError('Could not load %s backend: %s'
-                                  % (backend, e))
-
-def parse(url, **kwargs):
-    """
-    Parse a mbox archive pointed to by url.
-    """
-
-    backend = kwargs.pop('backend')
-
-    fh = _getFileObjFromUrl(url)
-    backend = _getBackend(backend)
-    parser = backend.Parser(**kwargs)
-    return parser.parse(fh)

pmap/centos.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Module for parsing centos mail archives.
-"""
-
-import re
-import logging
-log = logging.getLogger('pmap.centos')
-
-from pmap.common import BaseParser
-from pmap.common import BaseContainer
-
-class CentOSAdvisory(BaseContainer):
-    """
-    Container class for CentOS advisories.
-    """
-
-    _slots = ('discard', 'archs', 'header', 'pkgs', 'upstreamAdvisoryUrl', )
-
-    def finalize(self):
-        """
-        Derive some infomration used for advisories.
-        """
-
-        # E1101: Instance of 'CentOSAdvisory' has no 'upstreamAdvisoryUrl'
-        #        member
-        # W0201: Attribute 'description' defined outside __init__
-        # W0201: Attribute 'summary' defined outside __init__
-        # pylint: disable-msg=E1101
-        # pylint: disable-msg=W0201
-
-        BaseContainer.finalize(self)
-
-        assert self.subject is not None
-        self.summary = self.subject
-        self.description = self.upstreamAdvisoryUrl
-
-
-class Parser(BaseParser):
-    """
-    Parse for CentOS mail archives.
-    """
-
-    def __init__(self, productVersion=None):
-        BaseParser.__init__(self)
-
-        self._containerClass = CentOSAdvisory
-        self._states.update({
-            'rhnurl'            : self._rhnurl,
-            'updates'           : self._updates,
-            'supportedarch'     : self._supportedarch,
-            'unsupportedarch'   : self._unsupportedarch,
-            'centos'            : self._header,
-            'upstream'          : self._rhnurl,
-            'sha1'              : self._sha1,
-        })
-
-        self._supportedArchRE = '(noarch|src|i386|i686|x86_64)'
-        self._suparch = re.compile(self._supportedArchRE)
-
-        self._filter('^.*rhn\.redhat\.com.*$', 'rhnurl')
-        self._filter('^updates.*', 'updates')
-        self._filter('^%s' % self._supportedArchRE, 'supportedarch')
-        self._filter('(ia64|s390|s390x)', 'unsupportedarch')
-        self._filter('^[a-z0-9]{32}$', 'sha1')
-
-    def _newContainer(self):
-        """
-        Discard the current container if the discard flag is set.
-        """
-
-        if self._curObj and self._curObj.discard:
-            self._curObj = None
-        BaseParser._newContainer(self)
-
-    def _discard(self, force=False):
-        """
-        Set the current object to be discarded.
-        """
-
-        if self._curObj.discard is None or force:
-            log.debug('discarding message: %s' % self._curObj.subject)
-            self._curObj.discard = True
-
-    def _addPkg(self, pkg):
-        """
-        Add package to container.
-        """
-
-        if self._curObj.pkgs is None:
-            self._curObj.pkgs = set()
-        if not pkg.endswith('.rpm'):
-            return
-        self._curObj.pkgs.add(pkg)
-
-    def _rhnurl(self):
-        """
-        Set the rhn url.
-        """
-
-        line = self._getFullLine()
-        self._curObj.upstreamAdvisoryUrl = \
-            line[line.find('http'):line.find('html')+4]
-
-    def _updates(self):
-        """
-        Parse updates.
-        """
-
-        pkg = self._getFullLine().split('/')[-1]
-        self._addPkg(pkg)
-
-    def _supportedarch(self):
-        """
-        Filter on supported architectures.
-        """
-
-        self._curObj.discard = False
-        if self._curObj.archs is None:
-            self._curObj.archs = set()
-        self._curObj.archs.add(self._line[0])
-
-    def _unsupportedarch(self):
-        """
-        Discard messages that contain unsupported arches.
-        """
-
-        self._discard()
-
-    def _header(self):
-        """
-        Parse the header.
-        """
-
-        if self._line[1] == 'Errata' and self._line[3] == 'Security':
-            self._curObj.header = self._getFullLine()
-
-    def _sha1(self):
-        """
-        Parse sha1 lines.
-        """
-
-        self._addPkg(self._line[-1])

pmap/common.py

-#
-# Copyright (c) 2008-2009 rPath, Inc.
-#
-# This program is distributed under the terms of the Common Public License,
-# version 1.0. A copy of this license should have been distributed with this
-# source file in a file called LICENSE. If it is not present, the license
-# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
-#
-# This program is distributed in the hope that it will be useful, but
-# without any warranty; without even the implied warranty of merchantability
-# or fitness for a particular purpose. See the Common Public License for
-# full details.
-#
-
-"""
-Common parser module for parsing mbox mail archives.
-"""
-
-import shutil
-import mailbox
-import tempfile
-
-from aptmd.container import Container
-from aptmd.parser import ContainerizedParser as Parser
-
-class BaseContainer(Container):
-    """
-    Base MBox Message container class.
-    """
-
-    _slots = ('fromAddr', 'fromName', 'timestamp', 'subject', 'msg',
-              'description', 'summary', 'packages')
-
-    def __repr__(self):
-        return self.subject
-
-
-class BaseParser(Parser):
-    """
-    Base MBox parsing class.
-    """
-
-    def __init__(self):
-        Parser.__init__(self)
-
-        self._curMsg = None
-        self._containerClass = BaseContainer
-        self._states.update({
-        })
-
-    def parse(self, fileObj):
-        """
-        Parse file or file like objects.
-        """