|
|
|
@ -38,7 +38,7 @@ from collections import Mapping, defaultdict
|
|
|
|
|
from .common import getTupleVersion
|
|
|
|
|
from contextlib import closing
|
|
|
|
|
from functools import total_ordering
|
|
|
|
|
from itertools import ifilter, imap, chain
|
|
|
|
|
from itertools import ifilter, imap, chain, groupby
|
|
|
|
|
|
|
|
|
|
Colors = TextState.Colors
|
|
|
|
|
import glob
|
|
|
|
@ -762,6 +762,7 @@ class Eix(object):
|
|
|
|
|
Xml = '--xml'
|
|
|
|
|
Upgrade = '--upgrade'
|
|
|
|
|
TestObsolete = '--test-obsolete'
|
|
|
|
|
Exact = "--exact"
|
|
|
|
|
|
|
|
|
|
default_options = [Option.Xml]
|
|
|
|
|
|
|
|
|
@ -770,20 +771,22 @@ class Eix(object):
|
|
|
|
|
self.package = list(package)
|
|
|
|
|
else:
|
|
|
|
|
self.package = [package]
|
|
|
|
|
self.options = list(options) + self.package + self.default_options
|
|
|
|
|
self._options = options
|
|
|
|
|
self.no_default = kwargs.get('no_default', False)
|
|
|
|
|
if not kwargs.get('all_versions', False):
|
|
|
|
|
self.__get_versions = self._get_versions
|
|
|
|
|
self._get_versions = self._get_best_version
|
|
|
|
|
self.parser = EixFullnameParserBestVersion()
|
|
|
|
|
else:
|
|
|
|
|
self.parser = EixFullnameParser()
|
|
|
|
|
|
|
|
|
|
def _get_best_version(self, et):
|
|
|
|
|
ret = None
|
|
|
|
|
for ver in ifilter(lambda x: x.find('mask') is None,
|
|
|
|
|
et.iterfind('version')):
|
|
|
|
|
ret = ver.attrib['id']
|
|
|
|
|
yield ret
|
|
|
|
|
@property
|
|
|
|
|
def options(self):
|
|
|
|
|
if self.no_default:
|
|
|
|
|
return list(self._options) + self.package
|
|
|
|
|
else:
|
|
|
|
|
return list(self._options) + self.package + self.default_options
|
|
|
|
|
|
|
|
|
|
def _process(self):
|
|
|
|
|
return process(self.cmd, *self.options)
|
|
|
|
|
return process(self.cmd, *self.options, env={"LANG": "C"})
|
|
|
|
|
|
|
|
|
|
def get_output(self):
|
|
|
|
|
"""
|
|
|
|
@ -796,31 +799,153 @@ class Eix(object):
|
|
|
|
|
"""
|
|
|
|
|
Получить список пакетов
|
|
|
|
|
"""
|
|
|
|
|
return list(self._parseXml(self.get_output()))
|
|
|
|
|
return list(self.parser.parseXml(self.get_output()))
|
|
|
|
|
|
|
|
|
|
def _get_versions(self, et):
|
|
|
|
|
|
|
|
|
|
class EixParser(object):
|
|
|
|
|
"""
|
|
|
|
|
Парсер XML вывода от eix
|
|
|
|
|
"""
|
|
|
|
|
def parseXml(self, buffer):
|
|
|
|
|
try:
|
|
|
|
|
eix_xml = ET.fromstring(buffer)
|
|
|
|
|
return self.get_categories(eix_xml)
|
|
|
|
|
except ET.ParseError:
|
|
|
|
|
return iter(())
|
|
|
|
|
|
|
|
|
|
def get_categories(self, et):
|
|
|
|
|
raise StopIteration()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EixFullnameParser(EixParser):
|
|
|
|
|
"""
|
|
|
|
|
Получить все версии пакета
|
|
|
|
|
"""
|
|
|
|
|
def get_versions(self, et):
|
|
|
|
|
for ver in et.iterfind('version'):
|
|
|
|
|
yield ver.attrib['id']
|
|
|
|
|
|
|
|
|
|
def _get_packages(self, et):
|
|
|
|
|
def get_packages(self, et):
|
|
|
|
|
for pkg in et:
|
|
|
|
|
for version in self._get_versions(pkg):
|
|
|
|
|
for version in self.get_versions(pkg):
|
|
|
|
|
if version:
|
|
|
|
|
yield "%s-%s" % (pkg.attrib['name'], version)
|
|
|
|
|
else:
|
|
|
|
|
yield pkg.attrib['name']
|
|
|
|
|
|
|
|
|
|
def _get_categories(self, et):
|
|
|
|
|
def get_categories(self, et):
|
|
|
|
|
for category in et:
|
|
|
|
|
for pkg in self._get_packages(category):
|
|
|
|
|
for pkg in self.get_packages(category):
|
|
|
|
|
yield EmergePackage("%s/%s" % (category.attrib['name'], pkg))
|
|
|
|
|
|
|
|
|
|
def _parseXml(self, buffer):
|
|
|
|
|
try:
|
|
|
|
|
eix_xml = ET.fromstring(buffer)
|
|
|
|
|
return self._get_categories(eix_xml)
|
|
|
|
|
except ET.ParseError:
|
|
|
|
|
return iter(())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EixFullnameParserBestVersion(EixFullnameParser):
|
|
|
|
|
"""
|
|
|
|
|
Получить только одну максимальную версию пакета
|
|
|
|
|
"""
|
|
|
|
|
def get_versions(self, et):
|
|
|
|
|
ret = None
|
|
|
|
|
for ver in ifilter(lambda x: x.find('mask') is None,
|
|
|
|
|
et.iterfind('version')):
|
|
|
|
|
ret = ver.attrib['id']
|
|
|
|
|
yield ret
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PackageVersionInfo(object):
|
|
|
|
|
"""
|
|
|
|
|
Информация о версии пакета
|
|
|
|
|
"""
|
|
|
|
|
class Mask(object):
|
|
|
|
|
Keyword = 1
|
|
|
|
|
MissingKeyword = 2
|
|
|
|
|
Hardmask = 3
|
|
|
|
|
|
|
|
|
|
def __init__(self, pn, version):
|
|
|
|
|
self.pn = pn
|
|
|
|
|
self.slot = ""
|
|
|
|
|
self.version = version
|
|
|
|
|
self.mask = []
|
|
|
|
|
self.installed = False
|
|
|
|
|
|
|
|
|
|
def clone(self):
|
|
|
|
|
obj = PackageVersionInfo(self.pn, self.version)
|
|
|
|
|
obj.mask = list(self.mask)
|
|
|
|
|
obj.installed = self.installed
|
|
|
|
|
obj.slot = self.slot
|
|
|
|
|
return obj
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def stable(self):
|
|
|
|
|
return not self.mask
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def hardmask(self):
|
|
|
|
|
return PackageVersionInfo.Mask.Hardmask in self.mask
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def unstable(self):
|
|
|
|
|
return any(x in (PackageVersionInfo.Mask.Keyword,
|
|
|
|
|
PackageVersionInfo.Mask.MissingKeyword)
|
|
|
|
|
for x in self.mask)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def unstable_keyword(self):
|
|
|
|
|
return PackageVersionInfo.Mask.Keyword in self.mask
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def missing_keyword(self):
|
|
|
|
|
return PackageVersionInfo.Mask.MissingKeyword in self.mask
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return "<{pn}-{pv}:{slot} {attrs}>".format(
|
|
|
|
|
pn=self.pn,
|
|
|
|
|
pv=self.version,
|
|
|
|
|
slot=self.slot,
|
|
|
|
|
attrs=" ".join(x for x in ("stable", "hardmask",
|
|
|
|
|
"unstable", "unstable_keyword",
|
|
|
|
|
"installed",
|
|
|
|
|
"missing_keyword") if getattr(self, x)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class EixVersionParser(EixParser):
|
|
|
|
|
"""
|
|
|
|
|
Возвращает информацию о версии
|
|
|
|
|
"""
|
|
|
|
|
mask_map = {
|
|
|
|
|
'hard': PackageVersionInfo.Mask.Hardmask,
|
|
|
|
|
'keyword': PackageVersionInfo.Mask.Keyword,
|
|
|
|
|
'missing_keyword': PackageVersionInfo.Mask.MissingKeyword,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def get_categories(self, et):
|
|
|
|
|
for category in et:
|
|
|
|
|
for version in self.get_packages(category):
|
|
|
|
|
yield version
|
|
|
|
|
|
|
|
|
|
def get_packages(self, et):
|
|
|
|
|
for pkg in et:
|
|
|
|
|
pvi = PackageVersionInfo("%s/%s"%
|
|
|
|
|
(et.attrib['name'],
|
|
|
|
|
pkg.attrib['name']), None)
|
|
|
|
|
for version in self.get_versions(pkg, pvi):
|
|
|
|
|
yield version
|
|
|
|
|
|
|
|
|
|
def get_versions(self, et, pvi):
|
|
|
|
|
for ver in et.iterfind('version'):
|
|
|
|
|
retver = pvi.clone()
|
|
|
|
|
retver.version = ver.attrib['id']
|
|
|
|
|
if "slot" in ver.attrib:
|
|
|
|
|
retver.slot = ver.attrib['slot']
|
|
|
|
|
else:
|
|
|
|
|
retver.slot = "0"
|
|
|
|
|
if "installed" in ver.attrib:
|
|
|
|
|
retver.installed = True
|
|
|
|
|
for verattr in ver.iterfind('mask'):
|
|
|
|
|
_type = verattr.get('type')
|
|
|
|
|
if _type in self.mask_map:
|
|
|
|
|
retver.mask.append(self.mask_map[_type])
|
|
|
|
|
yield retver
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChrootEix(Eix):
|
|
|
|
@ -1456,6 +1581,70 @@ class BinaryPackage(object):
|
|
|
|
|
def clear(self):
|
|
|
|
|
removeDir(self.work_dn)
|
|
|
|
|
|
|
|
|
|
class WorldPackage(object):
|
|
|
|
|
def __init__(self, package):
|
|
|
|
|
self.pkg = EmergePackage(package)
|
|
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
|
return hash("%s:%s" % (self.pkg["CATEGORY/PN"], self.pkg["SLOT!"]))
|
|
|
|
|
|
|
|
|
|
def __eq__(self, obj):
|
|
|
|
|
return (self.pkg["CATEGORY/PN"] == obj["CATEGORY/PN"] and
|
|
|
|
|
self.pkg["SLOT!"] == obj["SLOT!"])
|
|
|
|
|
|
|
|
|
|
def __getitem__(self, item):
|
|
|
|
|
return self.pkg[item]
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return str("%s:%s"%(self.pkg["CATEGORY/PN"], self.pkg["SLOT"])
|
|
|
|
|
if self.pkg["SLOT!"] else self.pkg["CATEGORY/PN"])
|
|
|
|
|
|
|
|
|
|
class WorldFile(object):
|
|
|
|
|
class DiffType(object):
|
|
|
|
|
Added = 0
|
|
|
|
|
Removed = 1
|
|
|
|
|
Omitted = 2
|
|
|
|
|
|
|
|
|
|
def __init__(self, data):
|
|
|
|
|
self.data = data
|
|
|
|
|
self.packages = {
|
|
|
|
|
WorldPackage(x)
|
|
|
|
|
for x in (x.strip() for x in self.data.split('\n')
|
|
|
|
|
if x.strip() and not x.startswith("#"))}
|
|
|
|
|
|
|
|
|
|
def diff_new(self, worldfile):
|
|
|
|
|
return worldfile.packages - self.packages
|
|
|
|
|
|
|
|
|
|
def diff_removed(self, worldfile):
|
|
|
|
|
return self.packages - worldfile.packages
|
|
|
|
|
|
|
|
|
|
def diff_omitted(self, worldfile):
|
|
|
|
|
return worldfile.packages & self.packages
|
|
|
|
|
|
|
|
|
|
def category_diff(self, worldfile):
|
|
|
|
|
for group, data in groupby(sorted(chain(
|
|
|
|
|
((WorldFile.DiffType.Added, x)
|
|
|
|
|
for x in self.diff_new(worldfile)),
|
|
|
|
|
((WorldFile.DiffType.Removed, x)
|
|
|
|
|
for x in self.diff_removed(worldfile)),
|
|
|
|
|
((WorldFile.DiffType.Omitted, x)
|
|
|
|
|
for x in self.diff_omitted(worldfile))),
|
|
|
|
|
key=lambda x: x[1]["CATEGORY/PN"]),
|
|
|
|
|
lambda x: x[1]["CATEGORY/PN"]):
|
|
|
|
|
data = list(data)
|
|
|
|
|
added = sorted((pkg for _type, pkg in data
|
|
|
|
|
if _type == WorldFile.DiffType.Added),
|
|
|
|
|
key=lambda x: str(x))
|
|
|
|
|
removed = sorted((pkg for _type, pkg in data
|
|
|
|
|
if _type == WorldFile.DiffType.Removed),
|
|
|
|
|
key=lambda x: str(x))
|
|
|
|
|
omitted = sorted((pkg for _type, pkg in data
|
|
|
|
|
if _type == WorldFile.DiffType.Omitted),
|
|
|
|
|
key=lambda x: str(x))
|
|
|
|
|
if added or removed:
|
|
|
|
|
yield (group, added, removed, omitted)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_binary_file(pkg, pkgdir):
|
|
|
|
|
"""
|
|
|
|
|
Получить имя файла бинарного пакета
|
|
|
|
|