def __get_best_version(self, package, base_url, pkgcache): """ Should return touple (base_url,package_version) with the best version found in cache. If base_url is not specified, all repositories will be checked """ cache_keys = _filter_base_urls(base_url, pkgcache) # Go trough all base_url keys best = None best_base_url = None for cache_key in cache_keys: cache = pkgcache.get(cache_key, {}) if package in cache: match = self.__pkg_best_match(cache[package]) if match: if not best: best = match # We're safe. this should not be assigned best_base_url = cache_key else: if DpkgVersion(match) > DpkgVersion(best): best = match best_base_url = cache_key if best is None: return (None, None) else: return best_base_url, str(best)
def __pkg_best_match(self, cache): """ Looks for best version available """ if len(cache) == 0: # WTF!? return None try: best = DpkgVersion(cache[0]['version']) except VersionError: self._logger.info("BadVersion: %s %s" % (cache[0]['package'], cache[0]['version'])) return None if len(cache) > 1: for pkg in cache: try: pkg_ver = DpkgVersion(pkg['version']) if pkg_ver > best: best = pkg_ver except VersionError: self._logger.info("BadVersion: %s %s" % (pkg['package'], pkg['version'])) continue return best
class DpkgDebPackage(LoggableObject): """This class represent complete information about Debian binary package""" def __init__(self, pkgfile=None): """ path -- path to .deb package """ self.control = DpkgParagraph() self.md5sums = None self.__md5files = None self.changes = None self.news = None self.files = None self.__raw_files = None self.path = None if pkgfile: self.path = os.path.abspath(pkgfile) if not os.path.isfile(self.path): raise DpkgDebPackageException("Unable to locate file: %s" % self.path) self.load_control() def load(self, path=None, getfiles=True, getchanges='both'): """ Loads .deb file for processing """ path_changed = False if not path and not self.path: raise DpkgDebPackageException("No deb file specified") if path: new_path = os.path.abspath(path) if new_path != self.path: self.path = new_path path_changed = True if not os.path.isfile(self.path): raise DpkgDebPackageException("Unable to locate file: %s" % self.path) if path_changed or not self.control: self.load_control() if getchanges: self.load_changes(getchanges) if getfiles: self.load_contents() def load_contents(self): """ Reads contents of .deb file into memory """ if self.path and os.path.isfile(self.path): self.__raw_files = self.__list_contents() if self.__raw_files: self.files = [fname[5] for fname in self.__raw_files] else: raise DpkgDebPackageException("Unable to locate file: %s" % self.path) def load_changes(self, getchanges='both'): """ Reads changelog and/or news information into memory """ if self.path and os.path.isfile(self.path): (news, changes) = self.__extract_changes(getchanges) self.news = news self.changes = changes else: raise DpkgDebPackageException("Unable to locate file: %s" % self.path) def load_control(self): """ Reads control information into memory """ if self.path and os.path.isfile(self.path): tempdir = self.__extract_control() fhdl = open(os.path.join(tempdir, "control"), "r") self.control = DpkgParagraph() self.control.load(fhdl) fhdl.close() if not self.__parse_md5sums(tempdir): self._logger.warning("Can't parse md5sums") else: self.__md5files = [xsum[1] for xsum in self.md5sums] shutil.rmtree(tempdir, True) else: raise DpkgDebPackageException("Unable to locate file: %s" % self.path) def __extract_changes(self, which, since_version=None): '''Extract changelog entries, news or both from the package. If since_version is specified, only return entries later than the specified version. returns a sequence of Changes objects.''' def changelog_variations(filename): """Return list of all possible changelog/news locations""" formats = [ 'usr/doc/*/%s.gz', 'usr/share/doc/*/%s.gz', 'usr/doc/*/%s', 'usr/share/doc/*/%s', './usr/doc/*/%s.gz', './usr/share/doc/*/%s.gz', './usr/doc/*/%s', './usr/share/doc/*/%s' ] return [format % filename for format in formats] news_filenames = changelog_variations('NEWS.Debian') changelog_filenames = changelog_variations('changelog.Debian') changelog_filenames_native = changelog_variations('changelog') filenames = [] if which == 'both' or which == 'news': filenames.extend(news_filenames) if which == 'both' or which == 'changelogs': filenames.extend(changelog_filenames) filenames.extend(changelog_filenames_native) tempdir = self.extract_contents(filenames) news = None for filename in news_filenames: news = self.__read_changelog(os.path.join(tempdir, filename), since_version) if news: break changelog = None for batch in (changelog_filenames, changelog_filenames_native): for filename in batch: changelog = self.__read_changelog( os.path.join(tempdir, filename), since_version) if changelog: break if changelog: break shutil.rmtree(tempdir, True) return (news, changelog) def __extract_control(self): """Extracts content of control.tar.gz from .deb package""" try: tempdir = tempfile.mkdtemp(prefix='dpkgdebpackage') except AttributeError: tempdir = tempfile.mktemp() os.mkdir(tempdir) extract_command = 'ar p %s control.tar.gz | tar zxf - -C %s 2>/dev/null' % ( self.path, tempdir) os.system(extract_command) return tempdir def extract_contents(self, filenames): """Extracts partial contents of Debian package to temporary directory""" try: tempdir = tempfile.mkdtemp(prefix='dpkgdebpackage') except AttributeError: tempdir = tempfile.mktemp() os.mkdir(tempdir) extract_command = 'ar p %s data.tar.gz |tar zxf - -C %s %s 2>/dev/null' % ( self.path, tempdir, ' '.join( ["'%s'" % filen for filen in filenames])) # tar exits unsuccessfully if _any_ of the files we wanted # were not available, so we can't do much with its status os.system(extract_command) return tempdir def __parse_md5sums(self, tempdir): """Parses md5sums file from extracted control section of debian package""" path = os.path.join(tempdir, "md5sums") if not os.access(path, os.R_OK): print "Can't open file %s" % path return False self.md5sums = [] fhdl = open(path, "r") for line in fhdl.readlines(): if line[33] != " ": print "33 is not a space.\n %s" % line # Something bad happend, unknown file format. fhdl.close() return False argl = [line[:32].strip(), line[34:].strip()] self.md5sums.append(argl) fhdl.close() return True def __list_contents(self): """Returns filelist of data.tar.gz""" (status, output) = commands.getstatusoutput( "ar p %s data.tar.gz | tar ztvf -" % self.path) if status != 0: return [] files = [line.split() for line in output.splitlines()] return files def __read_changelog(self, filename, since_version): """Read changelog up to specified version""" changelog_header = re.compile( '^\S+ \((?P<version>.*)\) .*;.*urgency=(?P<urgency>\w+).*') filenames = glob.glob(filename) fhdl = None for filename in filenames: try: if filename.endswith('.gz'): fhdl = gzip.GzipFile(filename) else: fhdl = open(filename) break except IOError, ioerr: if ioerr.errno == errno.ENOENT: pass else: raise if not fhdl: return None changes = '' is_debian_changelog = 0 for line in fhdl.readlines(): match = changelog_header.match(line) if match: is_debian_changelog = 1 if since_version: if DpkgVersion(match.group('version')) <= since_version: break changes += line fhdl.close() if not is_debian_changelog: return None return changes