#
#  $Id: misc.py,v 1.1 1999/05/16 08:58:54 rob Exp $
#
#  Copyright 1999 Rob Tillotson <robt@debian.org>
#  All Rights Reserved
#
#  Permission to use, copy, modify, and distribute this software and
#  its documentation for any purpose and without fee or royalty is
#  hereby granted, provided that the above copyright notice appear in
#  all copies and that both the copyright notice and this permission
#  notice appear in supporting documentation or portions thereof,
#  including modifications, that you you make.
#
#  THE AUTHOR ROB TILLOTSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
#  THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
#  AND FITNESS.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
#  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
#  RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
#  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
#  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE!
#
"""Miscellaneous utilities.
"""

__version__ = '$Id: misc.py,v 1.1 1999/05/16 08:58:54 rob Exp $'

__copyright__ = 'Copyright 1999 Rob Tillotson <robt@debian.org>'

import sys, os, string, imp, operator
from types import ClassType

def list_modules(path):
    """List all available modules in a given directory."""
    l = {}
    suff = map(lambda x: x[0], imp.get_suffixes())
    suff.sort(lambda x,y: cmp(y,x))
    for name in os.listdir(path):
	for s in suff:
	    if name[-len(s):] == s:
		l[name[:-len(s)]] = 1
		break

    return l.keys()

def is_package(path):
    if os.path.isdir(path):
	d = list_modules(path)
	if '__init__' in d: return 1

def list_packages(path):
    """List available packages in a given path."""
    l = []
    for name in os.listdir(path):
	p = os.path.join(path, name)
	if is_package(p): l.append(name)
    return l

def list_all_packages(path):
    """List available packages, recursively.

    Does not handle the use of the __path__ variable inside packages.
    """
    l = []
    for p in list_packages(path):
	l.append(p)
	ap = list_all_packages(os.path.join(path, p))
	m = map(lambda x, pkg=p: pkg+'.'+x, ap)
	l = l + m
    return l

def list_all_modules(path):
    """List available modules, recursively.

    Does not handle the use of the __path__ variable inside packages.
    """
    l = []
    for p in list_packages(path):
	l.append(p)
	l = l + map(lambda x, pkg=p: pkg+'.'+x,
		    list_all_modules(os.path.join(path, p)))
    m = list_modules(path)
    if '__init__' in m: m.remove('__init__')
    l = l + m
    return l

def list_package_modules(package, g=None):
    try:
	p = import_module(package, g)
    except:
	return []
    if not hasattr(p, '__path__'):
	return []  # is this right? can a package NOT have a __path__?
    l = []
    for pa in p.__path__:
	l = l + list_modules(pa)
    return l

def load_plugins(path, collection, g=None):
    l = []
    for pa in path:
	if collection:
	    if pa: pa = pa + '.' + collection
	    else: pa = collection
	modules = list_package_modules(pa, g)
	for mn in modules:
	    nn = mn
	    if mn == '__init__':
		continue
	    if pa: mn = pa + '.' + mn
	    try:
		m = import_module(mn, g)
		for o in m.__dict__.values():
		    if type(o) == ClassType and hasattr(o, 'is_plugin'):
			l.append((nn, o))
			break
	    except:
		pass
    return l

def list_plugins(path, collection, g=None):
    return map(lambda x: x[0], load_plugins(path, collection, g))

plugin_info_attributes = ('name', 'author', 'description', 'version',
			  'url', 'type', 'type_name', 'properties',
			  'options'
			  )
def list_plugin_info(path, collection, g=None):
    l = {}
    for n, o in load_plugins(path, collection, g):
	d = {}
	map(lambda k, d=d, o=o: operator.setitem(d,k,getattr(o,k)),
	    plugin_info_attributes)
	if not l.has_key(n):  # skip all but the first one found on path
	    l[n] = d
    return l	
    

def import_module(name, g=None):
    """Import a package or module; cribbed from the manual."""
    mod = __import__(name, g)
    components = string.split(name, '.')
    for comp in components[1:]:
	mod = getattr(mod, comp)
    return mod

def parent_classes(cls):
    l = []
    for b in cls.__bases__:
	l.append(b)
	l = l + parent_classes(b)
    return l
