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
|
import supybot.world as world
|
||||||
from imp import reload
|
from imp import reload
|
||||||
|
|
||||||
__version__ = "1.5.0"
|
__version__ = "1.6.0"
|
||||||
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
|
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
|
||||||
__contributors__ = {
|
__contributors__ = {
|
||||||
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Concept'],
|
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Concept'],
|
||||||
|
|
|
@ -33,30 +33,6 @@ def description(pkg):
|
||||||
return pkg['Description'].split('\n')[0]
|
return pkg['Description'].split('\n')[0]
|
||||||
return "Description not available"
|
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:
|
class Apt:
|
||||||
def __init__(self, plugin):
|
def __init__(self, plugin):
|
||||||
self.aptdir = plugin.registryValue('aptdir')
|
self.aptdir = plugin.registryValue('aptdir')
|
||||||
|
@ -67,6 +43,30 @@ class Apt:
|
||||||
if self.aptdir:
|
if self.aptdir:
|
||||||
self.distros = sorted([x[:-5] for x in os.listdir(self.aptdir) if x.endswith('.list')])
|
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):
|
def _parse(self, pkg):
|
||||||
parser = FeedParser()
|
parser = FeedParser()
|
||||||
parser.feed(pkg)
|
parser.feed(pkg)
|
||||||
|
@ -82,22 +82,20 @@ class Apt:
|
||||||
pkgTracURL = "https://packages.ubuntu.com"
|
pkgTracURL = "https://packages.ubuntu.com"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = apt_cache(self.aptdir, distro, ['search', '-n'], pkg)
|
data = self.apt_cache(distro, ['search', '-n'], pkg)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
data = e.output
|
data = e.output
|
||||||
if not data:
|
if not data:
|
||||||
if filelookup:
|
if filelookup:
|
||||||
try:
|
try:
|
||||||
data = apt_file(self.aptdir, distro, pkg).split()
|
data = self.apt_file(distro, pkg).split()
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError:
|
||||||
data = e.output
|
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:
|
||||||
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:
|
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 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))
|
return "File %s found in %s" % (pkg, ', '.join(data))
|
||||||
|
@ -109,15 +107,15 @@ class Apt:
|
||||||
else:
|
else:
|
||||||
return "Found: %s" % ', '.join(pkgs)
|
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:
|
if distro not in self.distros:
|
||||||
return "%r is not a valid release: %s" % (distro, ", ".join(self.distros))
|
return "%r is not a valid release: %s" % (distro, ", ".join(self.distros))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = apt_cache(self.aptdir, distro, ['show'], pkg)
|
data = self.apt_cache(distro, ['show'] if not isSource else ['showsrc', '--only-source'], pkg)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError:
|
||||||
data = e.output
|
data = ''
|
||||||
if not data or 'E: No packages found' in data:
|
if not data:
|
||||||
return 'Package %s does not exist in %s' % (pkg, distro)
|
return 'Package %s does not exist in %s' % (pkg, distro)
|
||||||
|
|
||||||
maxp = {'Version': '0~'}
|
maxp = {'Version': '0~'}
|
||||||
|
@ -126,12 +124,31 @@ class Apt:
|
||||||
if apt.apt_pkg.version_compare(maxp['Version'], p['Version']) <= 0:
|
if apt.apt_pkg.version_compare(maxp['Version'], p['Version']) <= 0:
|
||||||
maxp = p
|
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:
|
if not archlookup:
|
||||||
return maxp
|
return maxp
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data2 = apt_cache(self.aptdir, distro, ['showsrc'], pkg)
|
data2 = self.apt_cache(distro, ['showsrc', '--only-source'], maxp['Sourcepkg'])
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
|
data2 = ''
|
||||||
|
if not data2:
|
||||||
return maxp
|
return maxp
|
||||||
|
|
||||||
maxp2 = {'Version': '0~'}
|
maxp2 = {'Version': '0~'}
|
||||||
|
@ -149,22 +166,30 @@ class Apt:
|
||||||
|
|
||||||
return maxp
|
return maxp
|
||||||
|
|
||||||
def info(self, pkg, distro):
|
def info(self, pkg, distro, isSource):
|
||||||
maxp = self.raw_info(pkg, distro)
|
maxp = self.raw_info(pkg, distro, isSource)
|
||||||
if isinstance(maxp, str):
|
if isinstance(maxp, str):
|
||||||
return maxp
|
return maxp
|
||||||
return("%s (source: %s): %s. In component %s, is %s. Version %s (%s), package size %s kB, installed size %s kB%s" %
|
if isSource:
|
||||||
(maxp['Package'], maxp.get('Source', None) or maxp['Package'], description(maxp), component(maxp['Section']),
|
return "%s (%s, %s): Packages %s. Maintained by %s%s" % (
|
||||||
maxp['Priority'], maxp['Version'], distro, int((int(maxp['Size'])/102.4)+0.5)/10, maxp['Installed-Size'],
|
maxp['Package'], maxp['Version'], distro, maxp['Binary'],
|
||||||
". (Only available for %s)" % maxp['Architectures'] if maxp.get('Architectures', None) else ""))
|
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):
|
def depends(self, pkg, distro, isSource):
|
||||||
maxp = self.raw_info(pkg, distro, archlookup=False)
|
maxp = self.raw_info(pkg, distro, isSource, archlookup=False)
|
||||||
if isinstance(maxp, str):
|
if isinstance(maxp, str):
|
||||||
return maxp
|
return maxp
|
||||||
return("%s (version: %s, %s): Depends on %s%s" %
|
if isSource:
|
||||||
(maxp['Package'], maxp['Version'], distro, maxp.get('Depends', None) or "nothing",
|
return "%s (%s, %s): Build depends on %s" % (
|
||||||
". Recommends %s" % maxp['Recommends'] if maxp.get('Recommends', None) else ""))
|
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
|
# Simple test
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -102,6 +102,12 @@ class PackageInfo(callbacks.Plugin):
|
||||||
return (defaultRelease, "%s %s" % (release, rest))
|
return (defaultRelease, "%s %s" % (release, rest))
|
||||||
return (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):
|
def __handleRest(self, msg, target, reply, rest):
|
||||||
targeto = target
|
targeto = target
|
||||||
prefix = ''
|
prefix = ''
|
||||||
|
@ -131,7 +137,7 @@ class PackageInfo(callbacks.Plugin):
|
||||||
return (target, prefix + reply)
|
return (target, prefix + reply)
|
||||||
|
|
||||||
def real_info(self, irc, msg, args, package, release=None):
|
def real_info(self, irc, msg, args, package, release=None):
|
||||||
"""<package> [<release>]
|
"""<[src:]package> [<release>]
|
||||||
|
|
||||||
Look up information for <package>, optionally in <release>
|
Look up information for <package>, optionally in <release>
|
||||||
"""
|
"""
|
||||||
|
@ -141,15 +147,16 @@ class PackageInfo(callbacks.Plugin):
|
||||||
(release, rest) = self.__getRelease(irc, release, channel)
|
(release, rest) = self.__getRelease(irc, release, channel)
|
||||||
if not release:
|
if not release:
|
||||||
return
|
return
|
||||||
|
(package, isSource) = self.__getPackage(package)
|
||||||
target = ircutils.replyTo(msg)
|
target = ircutils.replyTo(msg)
|
||||||
reply = self.Apt.info(package, release)
|
reply = self.Apt.info(package, release, isSource)
|
||||||
if rest:
|
if rest:
|
||||||
(target, reply) = self.__handleRest(msg, target, reply, rest)
|
(target, reply) = self.__handleRest(msg, target, reply, rest)
|
||||||
queue(irc, target, reply)
|
queue(irc, target, reply)
|
||||||
info = wrap(real_info, ['anything', optional('text')])
|
info = wrap(real_info, ['anything', optional('text')])
|
||||||
|
|
||||||
def real_depends(self, irc, msg, args, package, release=None):
|
def real_depends(self, irc, msg, args, package, release=None):
|
||||||
"""<package> [<release>]
|
"""<[src:]package> [<release>]
|
||||||
|
|
||||||
Look up dependencies for <package>, optionally in <release>
|
Look up dependencies for <package>, optionally in <release>
|
||||||
"""
|
"""
|
||||||
|
@ -159,8 +166,9 @@ class PackageInfo(callbacks.Plugin):
|
||||||
(release, rest) = self.__getRelease(irc, release, channel)
|
(release, rest) = self.__getRelease(irc, release, channel)
|
||||||
if not release:
|
if not release:
|
||||||
return
|
return
|
||||||
|
(package, isSource) = self.__getPackage(package)
|
||||||
target = ircutils.replyTo(msg)
|
target = ircutils.replyTo(msg)
|
||||||
reply = self.Apt.depends(package, release)
|
reply = self.Apt.depends(package, release, isSource)
|
||||||
if rest:
|
if rest:
|
||||||
(target, reply) = self.__handleRest(msg, target, reply, rest)
|
(target, reply) = self.__handleRest(msg, target, reply, rest)
|
||||||
queue(irc, target, reply)
|
queue(irc, target, reply)
|
||||||
|
|
Loading…
Reference in New Issue