"""grouch.util

Miscellaneous utility functions.
"""

# created 2000/12/13, GPW
#
# Copyright (c) 2001 CNRI.  All Rights Reserved.

__revision__ = "$Id: util.py,v 1.9 2001/09/05 14:21:23 gward Exp $"

import sys
import __builtin__
from types import TypeType, ClassType, InstanceType


def is_class_object (klass):
    """Return true if 'klass' is a class object (either a regular Python
    class or an ExtensionClass).
    """
    # The first clause handles regular classes quickly; the second
    # is needed to accomodate ExtensionClasses
    return type(klass) is ClassType or hasattr(klass, '__bases__')

def is_instance_object (object):
    """Return true if 'object' is an instance of some class (either a
    regular Python class or an ExtensionClass).
    """
    return ((type(object) is InstanceType or
             (hasattr(object, '__class__') and
              not hasattr(object, '__bases__'))) and
            hasattr(object, '__dict__'))

def get_class_object (name):
    lastdot = name.rfind(".")
    if lastdot == -1:                   # no dots -- look in main module
        module_name = '__main__'
        klass_name = name
        module = sys.modules[module_name]
    else:
        module_name = name[:lastdot]
        klass_name = name[lastdot+1:]
        try:
            __import__(module_name)
        except ImportError, exc:
            raise ValueError, \
                  "error finding class %s: %s" % (name, exc)
        module = sys.modules[module_name]

    if not hasattr(module, klass_name):
        raise ValueError, \
              "no such class %s in module %s" % (`klass_name`, module_name)
    else:
        klass = getattr(module, klass_name)
        if is_class_object(klass):
            return klass
        else:
            raise ValueError, \
                  "%s in module %s: not a class" % (`klass_name`, module_name)


def get_full_classname (klass):
    classname = klass.__name__
    modname = klass.__module__
    if modname == "__main__":
        return classname
    else:
        return "%s.%s" % (modname, classname)


# Supposedly, these two won't be necessary with Python 2.1, since its
# 'issubclass()' and 'isinstance()' will finally be able to deal with
# extension classes completely in 2.1.  (It's close in 1.6 and 2.0, but not
# quite there yet.)  I have not yet verified this!

def issubclass (class1, class2):
    """A version of 'issubclass()' that works with extension classes
    as well as regular Python classes.
    """

    # Both class objects are regular Python classes, so use the
    # built-in 'issubclass()'.
    if type(class1) is ClassType and type(class2) is ClassType:
        return __builtin__.issubclass(class1, class2)

    # Both so-called class objects have a '__bases__' attribute: ie.,
    # they aren't regular Python classes, but they sure look like them.
    # Assume they are extension classes and reimplement what the builtin
    # 'issubclass()' does behind the scenes.
    elif hasattr(class1, '__bases__') and hasattr(class2, '__bases__'):
        # XXX it appears that "ec.__class__ is type(ec)" for an
        # extension class 'ec': could we/should we use this as an
        # additional check for extension classes?

        # Breadth-first traversal of class1's superclass tree.  Order
        # doesn't matter because we're just looking for a "yes/no"
        # answer from the tree; if we were trying to resolve a name,
        # order would be important!
        stack = [class1]
        while stack:
            if stack[0] is class2:
                return 1
            stack.extend(list(stack[0].__bases__))
            del stack[0]
        else:
            return 0

    # Not a regular class, not an extension class: blow up for consistency
    # with builtin 'issubclass()"
    else:
        raise TypeError, "arguments must be class or ExtensionClass objects"

# issubclass ()

def isinstance (object, klass):
    """A version of 'isinstance()' that works with extension classes
    as well as regular Python classes."""

    if type(klass) is TypeType:
        return __builtin__.isinstance(object, klass)
    elif hasattr(object, '__class__'):
        return issubclass(object.__class__, klass)
    else:
        return 0
