pidgin/purple-plugin-pack

closing merged branch
org.guifications.plugins
2017-04-05, Gary Kramlich
f3f3a9dcba56
file isExecutable
closing merged branch
#!/usr/bin/python
# plugin_pack.py - Helper script for obtaining info about the plugin pack
# Copyright (C) 2008 Gary Kramlich <grim@reaperworld.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
"""Usage: plugin_pack.py [OPTION...] command
Flags:
-a Load abusive plugins
-d Load default plugins
-i Load incomplate plugins
-f Load finch plugins
-p Load purple plugins
-P Load pidgin plugins
Commands:
"""
import ConfigParser
import getopt
import glob
import os.path
import string
import sys
webpage = 'http://plugins.guifications.org/'
def printerr(msg):
print >> sys.stderr, msg
class Plugin:
name = ''
directory = ''
type = ''
depends = []
provides = ''
summary = ''
description = ''
authors = []
introduced = ''
notes = ''
def __init__(self, directory, name, parser):
self.name = name
self.directory = directory
self.type = parser.get(name, 'type')
self.depends = parser.get(name, 'depends').split()
self.provides = parser.get(name, 'provides')
self.summary = parser.get(name, 'summary')
self.description = parser.get(name, 'description')
self.authors = parser.get(name, 'authors').split(',')
self.introduced = parser.get(name, 'introduced')
if parser.has_option(name, 'notes'):
self.notes = parser.get(name, 'notes')
if self.type != 'default' and self.type != 'incomplete' and self.type != 'abusive':
printerr('\'%s\' has an unknown type of \'%s\'!' % (self.name, self.type))
def __str__(self):
output = 'name: %s\n' % self.name
output += 'authors: %s\n' % string.join(self.authors, ', ')
output += 'type: %s\n' % self.type
output += 'depends: %s\n' % string.join(self.depends, ' ')
output += 'provides: %s\n' % self.provides
output += 'directory: %s\n' % self.directory
output += 'summary: %s\n' % self.summary
output += 'description: %s\n' % self.description
if self.notes:
output += 'notes: %s\n' % self.notes
return output
class PluginPack:
commands = {}
plugins = {}
def load_plugins(self, types, depends):
if len(types) == 0:
types = None
if len(depends) == 0:
depends = None
for file in glob.glob('*/plugins.cfg'):
parser = ConfigParser.ConfigParser()
try:
parser.read(file)
except ConfigParser.ParsingError, msg:
printerr('Failed to parse \'%s\':\n%s' % (file, msg))
continue
for plugin in parser.sections():
p = Plugin(os.path.dirname(file), plugin, parser)
# this is kind of hacky, but if we have types, we check to see
# if the type is in list of types to load.
if types and not p.type in types:
continue
# now we check if the give plugins depends match the search
# depends
if depends:
if len(set(depends).intersection(set(p.depends))) == 0:
continue
self.plugins[p.provides] = p
def list_type(self, type):
list = []
for name in self.plugins.keys():
plugin = self.plugins[name]
if plugin.type == type:
list.append(plugin)
list.sort()
return list
def list_dep(self, dep):
list = []
for name in self.plugins.keys():
plugin = self.plugins[name]
if dep in plugin.depends:
list.append(plugin)
list.sort()
return list
def print_names(self, list):
names = []
for plugin in list:
names.append(plugin.name)
print string.join(names, ',')
def default_plugins(self):
return self.list_type('default')
def abusive_plugins(self):
return self.list_type('abusive')
def incomplete_plugins(self):
return self.list_type('incomplete')
def purple_plugins(self):
return self.list_dep('purple')
def finch_plugins(self):
return self.list_dep('finch')
def pidgin_plugins(self):
return self.list_dep('pidgin')
def unique_dirs(self):
dirs = {}
for name in self.plugins.keys():
dirs[self.plugins[name].directory] = 1
dirs = dirs.keys()
dirs.sort()
return dirs
def help(self, args):
"""Displays information about other commands"""
try:
cmd = self.commands[args[0]]
print cmd.__doc__
except KeyError:
print 'command \'%s\' was not found' % args[0]
except IndexError:
print '%s' % (self.help.__doc__)
print
print 'help usage:'
print ' help <command>'
print
print 'Available commands:'
cmds = self.commands.keys()
cmds.remove('help')
cmds.sort()
print ' %s' % (string.join(cmds, ' '))
commands['help'] = help
def dist_dirs(self, args):
"""Displays a list of all plugin directories to included in the distribution"""
print string.join(self.unique_dirs(), ' ')
commands['dist_dirs'] = dist_dirs
def build_dirs(self, args):
"""Displays a list of the plugins that can be built"""
if len(args) != 2:
printerr('build_dirs expects 2 arguments:')
printerr('\ta comma separated list of dependencies')
printerr('\ta comma separated list of plugins to build')
sys.exit(1)
# store the external depedencies
externals = args[0].split(',')
deps = {}
# run through the provided dependencies, setting their dependencies to
# nothing since we know we already have them
for d in externals:
deps[d] = []
# now run through the plugins adding their deps to the dictionary
for name in self.plugins.keys():
plugin = self.plugins[name]
deps[plugin.provides] = plugin.depends
# run through the requested plugins and store their plugin instance in check
check = []
for provides in args[1].split(','):
try:
if provides == 'all':
defaults = []
for p in self.default_plugins():
defaults.append(p.provides)
check += defaults
continue
plugin = self.plugins[provides]
check.append(plugin.provides)
except KeyError:
continue
# convert our list of plugins to check into a set to remove dupes
#check = set(check)
# create our list of plugins to build
build = []
# now define a function to check our deps
def has_deps(provides):
# don't add anything to build more than once
if provides in build:
return True
try:
dep_list = deps[provides]
except KeyError:
return False
# now check the dependencies
for dep in dep_list:
if '|' in dep:
count = 0
for d in dep.split('|'):
if has_deps(d):
count += 1
if count == 0:
return False
else:
if not has_deps(dep):
return False
# make sure the provides isn't an external
if not provides in externals:
build.append(provides)
# everything checks out!
return True
# check all the plugins we were told to for their dependencies
for c in check:
has_deps(c)
# now create a list of all directories to build
output = []
for provides in build:
plugin = self.plugins[provides]
output.append(plugin.directory)
output.sort()
print "%s" % (string.join(output, ','))
commands['build_dirs'] = build_dirs
def list_plugins(self, args):
"""Displays a list similiar to 'dpkg -l' about the plugin pack"""
data = {}
# create an array for the widths, we initialize it to the lengths of
# the title strings. We ignore summary, since that one shouldn't
# matter.
widths = [4, 8, 0]
for p in self.plugins.keys():
plugin = self.plugins[p]
if plugin.type == 'abusive':
type = 'a'
elif plugin.type == 'incomplete':
type = 'i'
else:
type = 'd'
if 'finch' in plugin.depends:
ui = 'f'
elif 'pidgin' in plugin.depends:
ui = 'p'
elif 'purple' in plugin.depends:
ui = 'r'
else:
ui = 'u'
widths[0] = max(widths[0], len(plugin.name))
widths[1] = max(widths[1], len(plugin.provides))
widths[2] = max(widths[2], len(plugin.summary))
data[plugin.provides] = [type, ui, plugin.name, plugin.provides, plugin.summary]
print 'Type=Default/Incomplete/Abusive'
print '| UI=Finch/Pidgin/puRple/Unknown'
print '|/ Name%s Provides%s Summary' % (' ' * (widths[0] - 4), ' ' * (widths[1] - 8))
print '++-%s-%s-%s' % ('=' * (widths[0]), '=' * (widths[1]), '=' * (widths[2]))
# create the format var
fmt = '%%s%%s %%-%ds %%-%ds %%s' % (widths[0], widths[1]) #, widths[2])
# now loop through the list again, with everything formatted
list = data.keys()
list.sort()
for p in list:
d = data[p]
print fmt % (d[0], d[1], d[2], d[3], d[4])
commands['list'] = list_plugins
def config_file(self, args):
"""Outputs the contents for the file to be m4_include()'d from configure"""
uniqdirs = self.unique_dirs()
# add our --with-plugins option
print 'AC_ARG_WITH(plugins,'
print ' AC_HELP_STRING([--with-plugins], [what plugins to build]),'
print ' ,with_plugins=all)'
print 'if test -z $with_plugins ; then'
print '\twith_plugins=all'
print 'fi'
# determine and add our output files
print 'PP_DIST_DIRS="%s"' % (string.join(uniqdirs, ' '))
print 'AC_SUBST(PP_DIST_DIRS)'
print
print 'AC_CONFIG_FILES(['
for dir in uniqdirs:
print '\t%s/Makefile' % (dir)
print '])'
print
# setup a second call to determine the plugins to be built
print 'PP_BUILD=`$PYTHON $srcdir/plugin_pack.py build_dirs $DEPENDENCIES $with_plugins`'
print
print 'PP_BUILD_DIRS=`echo $PP_BUILD | sed \'s/,/\ /g\'`'
print 'AC_SUBST(PP_BUILD_DIRS)'
print
print 'PP_PURPLE_BUILD="$PYTHON $srcdir/plugin_pack.py -p show_names $PP_BUILD"'
print 'PP_PIDGIN_BUILD="$PYTHON $srcdir/plugin_pack.py -P show_names $PP_BUILD"'
print 'PP_FINCH_BUILD="$PYTHON $srcdir/plugin_pack.py -f show_names $PP_BUILD"'
commands['config_file'] = config_file
def dependency_graph(self, args):
"""Outputs a graphviz script showing plugin dependencies"""
def node_label(plugin):
node = plugin.provides.replace('-', '_')
label = plugin.name
return node, label
def print_plugins(list):
for plugin in list:
node, label = node_label(plugin)
print '\t%s[label="%s"];' % (node, label)
print 'digraph {'
print '\tlabel="Dependency Graph";'
print '\tlabelloc="t";'
print '\tsplines=TRUE;'
print '\toverlap=FALSE;'
print
print '\tnode[fontname="sans", fontsize="8", style="filled"];'
print
# run through the default plugins
print '\t/* default plugins */'
print '\tnode[fillcolor="palegreen",shape="tab"];'
print_plugins(self.default_plugins())
print
# run through the incomplete plugins
print '\t/* incomplete plugins */'
print '\tnode[fillcolor="lightyellow1",shape="note"];'
print_plugins(self.incomplete_plugins())
print
# run through the abusive plugins
print '\t/* abusive plugins */'
print '\tnode[fillcolor="lightpink",shape="octagon"];'
print_plugins(self.abusive_plugins())
print
# run through again, this time showing the relations
print '\t/* dependencies'
print '\t * exteranl ones that don\'t have nodes get colored to the following'
print '\t */'
print '\tnode[fillcolor="powderblue", shape="egg"];'
for name in self.plugins.keys():
plugin = self.plugins[name]
node, label = node_label(plugin)
for dep in plugin.depends:
dep = dep.replace('-', '_')
print '\t%s -> %s;' % (node, dep)
print '}'
commands['dependency_graph'] = dependency_graph
def debian_description(self, args):
"""Outputs the description for the Debian packages"""
print 'Description: %d useful plugins for Pidgin, Finch, and Purple' % len(self.plugins)
print ' The Plugin Pack is a collection of many simple-yet-useful plugins for Pidgin,'
print ' Finch, and Purple. You will find a summary of each plugin below. For more'
print ' about an individual plugin, please see %s' % webpage
print ' .'
print ' Note: not all %d of these plugins are currently usable' % len(self.plugins)
list = self.plugins.keys()
list.sort()
for key in list:
plugin = self.plugins[key]
print ' .'
print ' %s: %s' % (plugin.name, plugin.summary)
print ' .'
print ' .'
print ' Homepage: %s' % webpage
commands['debian_description'] = debian_description
def show_names(self, args):
"""Displays the names of the given comma separated list of provides"""
if len(args) == 0 or len(args[0]) == 0:
printerr('show_names expects a comma separated list of provides')
sys.exit(1)
provides = args[0].split(',')
if len(provides) == 0:
print "none"
line = " "
for provide in provides:
if not provide in self.plugins:
continue
name = self.plugins[provide].name
if len(line) + len(name) + 2 > 75:
print line.rstrip(',')
line = ' '
line += ' %s,' % name
if len(line) > 1:
print line.rstrip(',')
commands['show_names'] = show_names
def info(self, args):
"""Displays all information about the given plugins"""
for p in args:
try:
print self.plugins[p].__str__().strip()
except KeyError:
print 'Failed to find a plugin that provides \'%s\'' % (p)
print
commands['info'] = info
def stats(self, args):
"""Displays stats about the plugin pack"""
counts = {}
counts['total'] = len(self.plugins)
counts['default'] = len(self.default_plugins())
counts['incomplete'] = len(self.incomplete_plugins())
counts['abusive'] = len(self.abusive_plugins())
counts['purple'] = len(self.purple_plugins())
counts['finch'] = len(self.finch_plugins())
counts['pidgin'] = len(self.pidgin_plugins())
def value(val):
return "%3d (%6.2f%%)" % (val, (float(val) / float(counts['total'])) * 100.0)
print "Purple Plugin Pack Stats"
print ""
print "%d plugins in total" % (counts['total'])
print
print "Status:"
print " complete: %s" % (value(counts['default']))
print " incomplete: %s" % (value(counts['incomplete']))
print " abusive: %s" % (value(counts['abusive']))
print ""
print "Type:"
print " purple: %s" % (value(counts['purple']))
print " finch: %s" % (value(counts['finch']))
print " pidgin: %s" % (value(counts['pidgin']))
commands['stats'] = stats
def show_usage(pp, exitcode):
print __doc__
cmds = pp.commands.keys()
cmds.sort()
for cmd in cmds:
print " %-20s %s" % (cmd, pp.commands[cmd].__doc__)
print ""
sys.exit(exitcode)
def main():
# create our main instance
pp = PluginPack()
types = []
depends = []
try:
shortopts = 'adfiPp'
opts, args = getopt.getopt(sys.argv[1:], shortopts)
except getopt.error, msg:
print msg
show_usage(pp, 1)
for o, a in opts:
if o == '-a':
types.append('abusive')
elif o == '-d':
types.append('default')
elif o == '-i':
types.append('incomplete')
elif o == '-f':
depends.append('finch')
elif o == '-P':
depends.append('pidgin')
elif o == '-p':
depends.append('purple')
# load the plugins that have been requested, if both lists are empty, all
# plugins are loaded
pp.load_plugins(types, depends)
if(len(args) == 0):
show_usage(pp, 1)
cmd = args[0]
args = args[1:]
try:
pp.commands[cmd](pp, args)
except KeyError:
printerr('\'%s\' command not found' % (cmd))
if __name__ == '__main__':
# this is a work around when we're called for a directory that isn't the
# directory that this file is in. This happens during distcheck, as well
# as a few other cases that most people won't use ;)
if os.path.dirname(__file__) != '':
os.chdir(os.path.dirname(__file__))
main()