PackageInfo: Add option to look up source packages too.
* Improve error handling. * Improve output formatting.
This commit is contained in:
parent
9bf38fa948
commit
b27c23725b
|
@ -22,7 +22,7 @@ import supybot
|
|||
import supybot.world as world
|
||||
from imp import reload
|
||||
|
||||
__version__ = "1.5.0"
|
||||
__version__ = "1.6.0"
|
||||
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
|
||||
__contributors__ = {
|
||||
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Concept'],
|
||||
|
|
|
@ -33,30 +33,6 @@ def description(pkg):
|
|||
return pkg['Description'].split('\n')[0]
|
||||
return "Description not available"
|
||||
|
||||
def apt_cache(aptdir, distro, cmd, pkg):
|
||||
return subprocess.check_output(['apt-cache',
|
||||
'-oAPT::Architecture=amd64',
|
||||
'-oAPT::Architectures::=i386',
|
||||
'-oAPT::Architectures::=amd64',
|
||||
'-oDir::State::Lists=%s/%s' % (aptdir, distro),
|
||||
'-oDir::State::Status=%s/%s.status' % (aptdir, distro),
|
||||
'-oDir::Etc::SourceList=%s/%s.list' % (aptdir, distro),
|
||||
'-oDir::Etc::SourceParts=""',
|
||||
'-oDir::Cache=%s/cache' % aptdir] +
|
||||
cmd + [pkg.lower()]).decode('utf8')
|
||||
|
||||
def apt_file(aptdir, distro, pkg):
|
||||
return subprocess.check_output(['apt-file',
|
||||
'-oAPT::Architecture=amd64',
|
||||
'-oAPT::Architectures::=i386',
|
||||
'-oAPT::Architectures::=amd64',
|
||||
'-oDir::State::Lists=%s/%s' % (aptdir, distro),
|
||||
'-oDir::State::Status=%s/%s.status' % (aptdir, distro),
|
||||
'-oDir::Etc::SourceList=%s/%s.list' % (aptdir, distro),
|
||||
'-oDir::Etc::SourceParts=""',
|
||||
'-oDir::Cache=%s/cache' % aptdir,
|
||||
'-l', '-i', 'search', pkg]).decode('utf8')
|
||||
|
||||
class Apt:
|
||||
def __init__(self, plugin):
|
||||
self.aptdir = plugin.registryValue('aptdir')
|
||||
|
@ -67,6 +43,30 @@ class Apt:
|
|||
if self.aptdir:
|
||||
self.distros = sorted([x[:-5] for x in os.listdir(self.aptdir) if x.endswith('.list')])
|
||||
|
||||
def apt_cache(self, distro, cmd, pkg):
|
||||
return subprocess.check_output(['apt-cache',
|
||||
'-oAPT::Architecture=amd64',
|
||||
'-oAPT::Architectures::=i386',
|
||||
'-oAPT::Architectures::=amd64',
|
||||
'-oDir::State::Lists=%s/%s' % (self.aptdir, distro),
|
||||
'-oDir::State::Status=%s/%s.status' % (self.aptdir, distro),
|
||||
'-oDir::Etc::SourceList=%s/%s.list' % (self.aptdir, distro),
|
||||
'-oDir::Etc::SourceParts=""',
|
||||
'-oDir::Cache=%s/cache' % self.aptdir] +
|
||||
cmd + [pkg.lower()]).decode('utf-8')
|
||||
|
||||
def apt_file(self, distro, pkg):
|
||||
return subprocess.check_output(['apt-file',
|
||||
'-oAPT::Architecture=amd64',
|
||||
'-oAPT::Architectures::=i386',
|
||||
'-oAPT::Architectures::=amd64',
|
||||
'-oDir::State::Lists=%s/%s' % (self.aptdir, distro),
|
||||
'-oDir::State::Status=%s/%s.status' % (self.aptdir, distro),
|
||||
'-oDir::Etc::SourceList=%s/%s.list' % (self.aptdir, distro),
|
||||
'-oDir::Etc::SourceParts=""',
|
||||
'-oDir::Cache=%s/cache' % self.aptdir,
|
||||
'-l', '-i', 'search', pkg]).decode('utf-8')
|
||||
|
||||
def _parse(self, pkg):
|
||||
parser = FeedParser()
|
||||
parser.feed(pkg)
|
||||
|
@ -82,22 +82,20 @@ class Apt:
|
|||
pkgTracURL = "https://packages.ubuntu.com"
|
||||
|
||||
try:
|
||||
data = apt_cache(self.aptdir, distro, ['search', '-n'], pkg)
|
||||
data = self.apt_cache(distro, ['search', '-n'], pkg)
|
||||
except subprocess.CalledProcessError as e:
|
||||
data = e.output
|
||||
if not data:
|
||||
if filelookup:
|
||||
try:
|
||||
data = apt_file(self.aptdir, distro, pkg).split()
|
||||
except subprocess.CalledProcessError as e:
|
||||
data = e.output
|
||||
data = self.apt_file(distro, pkg).split()
|
||||
except subprocess.CalledProcessError:
|
||||
self.log.error("PackageInfo/packages: Please update the cache for %s" % distro)
|
||||
return "Cache out of date, please contact the administrator"
|
||||
except OSError:
|
||||
self.log.error("PackageInfo/packages: apt-file is not installed")
|
||||
return "Please use %s/ to search for files" % pkgTracURL
|
||||
if data:
|
||||
if data[0] == 'sh:': # apt-file isn't installed
|
||||
self.log.error("PackageInfo/packages: apt-file is not installed")
|
||||
return "Please use %s/ to search for files" % pkgTracURL
|
||||
if data[0] == 'E:': # No files in the cache dir
|
||||
self.log.error("PackageInfo/packages: Please update the cache for %s" % distro)
|
||||
return "Cache out of date, please contact the administrator"
|
||||
if len(data) > 10:
|
||||
return "File %s found in %s and %d others <%s/search?searchon=contents&keywords=%s&mode=exactfilename&suite=%s&arch=any>" % (pkg, ', '.join(data[:10]), len(data)-10, pkgTracURL, utils.web.urlquote(pkg), distro)
|
||||
return "File %s found in %s" % (pkg, ', '.join(data))
|
||||
|
@ -109,15 +107,15 @@ class Apt:
|
|||
else:
|
||||
return "Found: %s" % ', '.join(pkgs)
|
||||
|
||||
def raw_info(self, pkg, distro, archlookup=True):
|
||||
def raw_info(self, pkg, distro, isSource, archlookup=True):
|
||||
if distro not in self.distros:
|
||||
return "%r is not a valid release: %s" % (distro, ", ".join(self.distros))
|
||||
|
||||
try:
|
||||
data = apt_cache(self.aptdir, distro, ['show'], pkg)
|
||||
except subprocess.CalledProcessError as e:
|
||||
data = e.output
|
||||
if not data or 'E: No packages found' in data:
|
||||
data = self.apt_cache(distro, ['show'] if not isSource else ['showsrc', '--only-source'], pkg)
|
||||
except subprocess.CalledProcessError:
|
||||
data = ''
|
||||
if not data:
|
||||
return 'Package %s does not exist in %s' % (pkg, distro)
|
||||
|
||||
maxp = {'Version': '0~'}
|
||||
|
@ -126,12 +124,31 @@ class Apt:
|
|||
if apt.apt_pkg.version_compare(maxp['Version'], p['Version']) <= 0:
|
||||
maxp = p
|
||||
|
||||
if isSource:
|
||||
bdeps = maxp.get('Build-Depends')
|
||||
vcs = maxp.get('Vcs-Browser')
|
||||
for (key, value) in list(maxp.items()):
|
||||
if key.startswith('Build-Depends-'):
|
||||
bdeps = "%s, %s" % (bdeps, value) if bdeps else value
|
||||
elif key.startswith('Vcs-') and not vcs:
|
||||
vcs = "%s (%s)" % (value, key[4:])
|
||||
maxp['Builddeps'] = bdeps
|
||||
maxp['Vcs'] = vcs
|
||||
return maxp
|
||||
|
||||
if not maxp.get('Source'):
|
||||
maxp['Sourcepkg'] = maxp['Package']
|
||||
else:
|
||||
maxp['Sourcepkg'] = maxp['Source'].split()[0]
|
||||
|
||||
if not archlookup:
|
||||
return maxp
|
||||
|
||||
try:
|
||||
data2 = apt_cache(self.aptdir, distro, ['showsrc'], pkg)
|
||||
data2 = self.apt_cache(distro, ['showsrc', '--only-source'], maxp['Sourcepkg'])
|
||||
except subprocess.CalledProcessError:
|
||||
data2 = ''
|
||||
if not data2:
|
||||
return maxp
|
||||
|
||||
maxp2 = {'Version': '0~'}
|
||||
|
@ -149,22 +166,30 @@ class Apt:
|
|||
|
||||
return maxp
|
||||
|
||||
def info(self, pkg, distro):
|
||||
maxp = self.raw_info(pkg, distro)
|
||||
def info(self, pkg, distro, isSource):
|
||||
maxp = self.raw_info(pkg, distro, isSource)
|
||||
if isinstance(maxp, str):
|
||||
return maxp
|
||||
return("%s (source: %s): %s. In component %s, is %s. Version %s (%s), package size %s kB, installed size %s kB%s" %
|
||||
(maxp['Package'], maxp.get('Source', None) or maxp['Package'], description(maxp), component(maxp['Section']),
|
||||
maxp['Priority'], maxp['Version'], distro, int((int(maxp['Size'])/102.4)+0.5)/10, maxp['Installed-Size'],
|
||||
". (Only available for %s)" % maxp['Architectures'] if maxp.get('Architectures', None) else ""))
|
||||
if isSource:
|
||||
return "%s (%s, %s): Packages %s. Maintained by %s%s" % (
|
||||
maxp['Package'], maxp['Version'], distro, maxp['Binary'],
|
||||
re.sub(r' <\S+>$', '', maxp.get('Original-Maintainer', maxp['Maintainer'])),
|
||||
" @ %s" % maxp['Vcs'] if maxp['Vcs'] else "")
|
||||
return "{} ({}, {}): {}. In component {}, is {}. Built by {}. Size {:,} kB / {:,} kB{}".format(
|
||||
maxp['Package'], maxp['Version'], distro, description(maxp), component(maxp['Section']),
|
||||
maxp['Priority'], maxp['Sourcepkg'], int((int(maxp['Size'])/1024)+1), int(maxp['Installed-Size']),
|
||||
". (Only available for %s.)" % maxp['Architectures'] if maxp.get('Architectures') else "")
|
||||
|
||||
def depends(self, pkg, distro):
|
||||
maxp = self.raw_info(pkg, distro, archlookup=False)
|
||||
def depends(self, pkg, distro, isSource):
|
||||
maxp = self.raw_info(pkg, distro, isSource, archlookup=False)
|
||||
if isinstance(maxp, str):
|
||||
return maxp
|
||||
return("%s (version: %s, %s): Depends on %s%s" %
|
||||
(maxp['Package'], maxp['Version'], distro, maxp.get('Depends', None) or "nothing",
|
||||
". Recommends %s" % maxp['Recommends'] if maxp.get('Recommends', None) else ""))
|
||||
if isSource:
|
||||
return "%s (%s, %s): Build depends on %s" % (
|
||||
maxp['Package'], maxp['Version'], distro, maxp.get('Builddeps', "nothing"))
|
||||
return "%s (%s, %s): Depends on %s%s" % (
|
||||
maxp['Package'], maxp['Version'], distro, maxp.get('Depends', "nothing"),
|
||||
". Recommends %s" % maxp['Recommends'] if maxp.get('Recommends') else "")
|
||||
|
||||
# Simple test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -102,6 +102,12 @@ class PackageInfo(callbacks.Plugin):
|
|||
return (defaultRelease, "%s %s" % (release, rest))
|
||||
return (release, rest)
|
||||
|
||||
def __getPackage(self, package):
|
||||
if package.startswith('src:'):
|
||||
package = package[4:]
|
||||
return (package, True)
|
||||
return (package, False)
|
||||
|
||||
def __handleRest(self, msg, target, reply, rest):
|
||||
targeto = target
|
||||
prefix = ''
|
||||
|
@ -131,7 +137,7 @@ class PackageInfo(callbacks.Plugin):
|
|||
return (target, prefix + reply)
|
||||
|
||||
def real_info(self, irc, msg, args, package, release=None):
|
||||
"""<package> [<release>]
|
||||
"""<[src:]package> [<release>]
|
||||
|
||||
Look up information for <package>, optionally in <release>
|
||||
"""
|
||||
|
@ -141,15 +147,16 @@ class PackageInfo(callbacks.Plugin):
|
|||
(release, rest) = self.__getRelease(irc, release, channel)
|
||||
if not release:
|
||||
return
|
||||
(package, isSource) = self.__getPackage(package)
|
||||
target = ircutils.replyTo(msg)
|
||||
reply = self.Apt.info(package, release)
|
||||
reply = self.Apt.info(package, release, isSource)
|
||||
if rest:
|
||||
(target, reply) = self.__handleRest(msg, target, reply, rest)
|
||||
queue(irc, target, reply)
|
||||
info = wrap(real_info, ['anything', optional('text')])
|
||||
|
||||
def real_depends(self, irc, msg, args, package, release=None):
|
||||
"""<package> [<release>]
|
||||
"""<[src:]package> [<release>]
|
||||
|
||||
Look up dependencies for <package>, optionally in <release>
|
||||
"""
|
||||
|
@ -159,8 +166,9 @@ class PackageInfo(callbacks.Plugin):
|
|||
(release, rest) = self.__getRelease(irc, release, channel)
|
||||
if not release:
|
||||
return
|
||||
(package, isSource) = self.__getPackage(package)
|
||||
target = ircutils.replyTo(msg)
|
||||
reply = self.Apt.depends(package, release)
|
||||
reply = self.Apt.depends(package, release, isSource)
|
||||
if rest:
|
||||
(target, reply) = self.__handleRest(msg, target, reply, rest)
|
||||
queue(irc, target, reply)
|
||||
|
|
Loading…
Reference in New Issue