# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Python Computer Graphics Kit.
#
# The Initial Developer of the Original Code is Matthias Baas.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
# $Id: call.py,v 1.1.1.1 2004/12/12 14:31:33 mbaas Exp $

## \file call.py
## Contains the Call class.

import string, types


# Call
class Call:
    """Call adapter.

    This class can either be used to wrap a string or a callable
    object with a fixed set of arguments (positional and keyword
    arguments).  When the %Call object is called (without any
    arguments) it executes the string or calls the wrapped function
    with the stored arguments.

    Objects of this class can be compared using == or != and they're
    hashable (so can be used in dictionaries, etc.)

    The purpose of this class is to provide an adapter for GUI commands
    which take no arguments.

    Example:

    \code

    def foo(a, b, c=0):
        print "a:%s  b:%s  c:%s"%(a,b,c)

    >>> f = Call(foo, 1, 2, c=3)
    
    # Call foo with the specified arguments
    >>> f()
    a:1  b:2  c:3
    
    # Print a readable description of the command object
    >>> print f
    foo(1, 2, c=3)

    \endcode

    """
    
    def __init__(self, func, *args, **keyargs):
        """Constructor.

        You must provide the callable object as first argument. All
        additional arguments will be passed to the callable object
        when its called.

        If \a func is a string then there may be one additional argument
        that specifies a dictionary which should be used as global
        (and local) namespace. The dictionary may be specified as positional
        argument following the string or as keyword argument "globals".

        The constructor sets the variable __doc__ to a reasonable value.
        It'll be either the function doc string if there is one or the
        command string itself.

        \param func (\c callable or \c str) The function that should be called
        \param *args  The positional arguments (default = no args)
        \param **keyargs  The keyword arguments (default = no keyword args)
        """
        if isinstance(func, types.StringTypes):
            self._func = func
            self._args = None
            self._keyargs = None
            compilestr = func
            if "globals" in keyargs:
                self._globals = keyargs["globals"]
            elif len(args)==1:
                self._globals = args[0]
            self.__doc__ = None
        elif callable(func):
            self._func = func
            self._args = args
            self._keyargs = keyargs
            compilestr = "apply(self._func, self._args, self._keyargs)"
            self.__doc__ = getattr(func, "__doc__", None)
        else:
            raise TypeError, "The 'func' argument must be a string or a callable object."

        self._code = compile(compilestr, "<command: '%s'>"%str(self), "exec")
        if self.__doc__==None or self.__doc__=="":
            self.__doc__ = str(self)

    def __str__(self):
        if callable(self._func):
            res = getattr(self._func, "__name__", "<unknown>")
            args = map(lambda x: str(x), self._args)
            args += map(lambda x: "%s=%s"%(x,self._keyargs[x]), self._keyargs)
            res = "%s(%s)"%(res,string.join(args,", "))
        else:
            res = self._func
        return res

    def __repr__(self):
        return "<Call cmd='%s'>"%str(self)

    def __call__(self):
        if hasattr(self, "_globals"):
            exec self._code in self._globals
        else:
            exec self._code

    def __eq__(self, other):
        if not isinstance(other, Call):
            return False

        return (self._func==other._func and
                self._args==other._args and
                self._keyargs==other._keyargs)

    def __ne__(self, other):
        if not isinstance(other, Call):
            return True

        return (self._func!=other._func or
                self._args!=other._args or
                self._keyargs!=other._keyargs)

    def __hash__(self):
        return hash(self._code)
#        return hash(self._func) ^ hash(self._args)


