2010-01-18 16:12:10 +01:00
|
|
|
"""Module for reading repodata directory (created with createrepo) for package
|
|
|
|
information instead of scanning individual rpms."""
|
|
|
|
|
|
|
|
# standard modules
|
|
|
|
import gzip
|
|
|
|
import os.path
|
|
|
|
|
|
|
|
# cElementTree can be standard or 3rd-party depending on python version
|
|
|
|
try:
|
|
|
|
from xml.etree import cElementTree as ET
|
|
|
|
except ImportError:
|
|
|
|
import cElementTree as ET
|
|
|
|
|
|
|
|
# project modules
|
|
|
|
import osc.util.rpmquery
|
2015-06-23 17:52:37 +02:00
|
|
|
import osc.util.packagequery
|
2010-01-18 16:12:10 +01:00
|
|
|
|
|
|
|
def namespace(name):
|
|
|
|
return "{http://linux.duke.edu/metadata/%s}" % name
|
|
|
|
|
|
|
|
OPERATOR_BY_FLAGS = {
|
|
|
|
"EQ" : "=",
|
|
|
|
"LE" : "<=",
|
|
|
|
"GE" : ">="
|
|
|
|
}
|
|
|
|
|
|
|
|
def primaryPath(directory):
|
|
|
|
"""Returns path to the primary repository data file.
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
@param directory repository directory that contains the repodata subdirectory
|
|
|
|
@return str path to primary repository data file
|
|
|
|
@raise IOError if repomd.xml contains no primary location
|
|
|
|
"""
|
|
|
|
metaDataPath = os.path.join(directory, "repodata", "repomd.xml")
|
|
|
|
elementTree = ET.parse(metaDataPath)
|
|
|
|
root = elementTree.getroot()
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
for dataElement in root:
|
|
|
|
if dataElement.get("type") == "primary":
|
|
|
|
locationElement = dataElement.find(namespace("repo") + "location")
|
|
|
|
# even though the repomd.xml file is under repodata, the location a
|
|
|
|
# attribute is relative to parent directory (directory).
|
|
|
|
primaryPath = os.path.join(directory, locationElement.get("href"))
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise IOError("'%s' contains no primary location" % metaDataPath)
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
return primaryPath
|
|
|
|
|
|
|
|
def queries(directory):
|
|
|
|
"""Returns a list of RepoDataQueries constructed from the repodata under
|
|
|
|
the directory.
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
@param directory path to a repository directory (parent directory of
|
|
|
|
repodata directory)
|
2015-06-23 17:52:37 +02:00
|
|
|
@return list of RepoDataQueryResult instances
|
2010-01-18 16:12:10 +01:00
|
|
|
@raise IOError if repomd.xml contains no primary location
|
|
|
|
"""
|
|
|
|
path = primaryPath(directory)
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
gunzippedPrimary = gzip.GzipFile(path)
|
|
|
|
elementTree = ET.parse(gunzippedPrimary)
|
|
|
|
root = elementTree.getroot()
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
packageQueries = []
|
|
|
|
for packageElement in root:
|
2015-06-23 17:52:37 +02:00
|
|
|
packageQuery = RepoDataQueryResult(directory, packageElement)
|
2010-01-18 16:12:10 +01:00
|
|
|
packageQueries.append(packageQuery)
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
return packageQueries
|
|
|
|
|
2015-06-23 17:52:37 +02:00
|
|
|
class RepoDataQueryResult(osc.util.packagequery.PackageQueryResult):
|
|
|
|
"""PackageQueryResult that reads in data from the repodata directory files."""
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def __init__(self, directory, element):
|
2015-06-23 17:52:37 +02:00
|
|
|
"""Creates a RepoDataQueryResult from the a package Element under a metadata
|
2010-01-18 16:12:10 +01:00
|
|
|
Element in a primary.xml file.
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
@param directory repository directory path. Used to convert relative
|
|
|
|
paths to full paths.
|
|
|
|
@param element package Element
|
|
|
|
"""
|
|
|
|
self.__directory = os.path.abspath(directory)
|
|
|
|
self.__element = element
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def __formatElement(self):
|
|
|
|
return self.__element.find(namespace("common") + "format")
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def __parseEntry(self, element):
|
|
|
|
entry = element.get("name")
|
|
|
|
flags = element.get("flags")
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
if flags is not None:
|
|
|
|
version = element.get("ver")
|
|
|
|
operator = OPERATOR_BY_FLAGS[flags]
|
|
|
|
entry += " %s %s" % (operator, version)
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
release = element.get("rel")
|
|
|
|
if release is not None:
|
2010-02-05 15:14:48 +01:00
|
|
|
entry += "-%s" % release
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
return entry
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def __parseEntryCollection(self, collection):
|
|
|
|
formatElement = self.__formatElement()
|
|
|
|
collectionElement = formatElement.find(namespace("rpm") + collection)
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
entries = []
|
|
|
|
if collectionElement is not None:
|
|
|
|
for entryElement in collectionElement.findall(namespace("rpm") +
|
|
|
|
"entry"):
|
|
|
|
entry = self.__parseEntry(entryElement)
|
|
|
|
entries.append(entry)
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
return entries
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def __versionElement(self):
|
|
|
|
return self.__element.find(namespace("common") + "version")
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def arch(self):
|
|
|
|
return self.__element.find(namespace("common") + "arch").text
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def description(self):
|
|
|
|
return self.__element.find(namespace("common") + "description").text
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def distribution(self):
|
|
|
|
return None
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def epoch(self):
|
|
|
|
return self.__versionElement().get("epoch")
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def name(self):
|
|
|
|
return self.__element.find(namespace("common") + "name").text
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def path(self):
|
|
|
|
locationElement = self.__element.find(namespace("common") + "location")
|
|
|
|
relativePath = locationElement.get("href")
|
|
|
|
absolutePath = os.path.join(self.__directory, relativePath)
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
return absolutePath
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def provides(self):
|
|
|
|
return self.__parseEntryCollection("provides")
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def release(self):
|
|
|
|
return self.__versionElement().get("rel")
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def requires(self):
|
|
|
|
return self.__parseEntryCollection("requires")
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2015-06-23 17:52:37 +02:00
|
|
|
def conflicts(self):
|
|
|
|
return self.__parseEntryCollection('conflicts')
|
|
|
|
|
|
|
|
def obsoletes(self):
|
|
|
|
return self.__parseEntryCollection('obsoletes')
|
|
|
|
|
|
|
|
def canonname(self):
|
|
|
|
return osc.util.rpmquery.RpmQuery.filename(self.name(), None,
|
|
|
|
self.version(), self.release(), self.arch())
|
|
|
|
|
|
|
|
def gettag(self, tag):
|
|
|
|
# implement me, if needed
|
|
|
|
return None
|
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def vercmp(self, other):
|
|
|
|
res = osc.util.rpmquery.RpmQuery.rpmvercmp(str(self.epoch()), str(other.epoch()))
|
|
|
|
if res != 0:
|
|
|
|
return res
|
|
|
|
res = osc.util.rpmquery.RpmQuery.rpmvercmp(self.version(), other.version())
|
|
|
|
if res != 0:
|
|
|
|
return res
|
|
|
|
res = osc.util.rpmquery.RpmQuery.rpmvercmp(self.release(), other.release())
|
|
|
|
return res
|
2010-02-28 02:30:13 +01:00
|
|
|
|
2010-01-18 16:12:10 +01:00
|
|
|
def version(self):
|
|
|
|
return self.__versionElement().get("ver")
|