# dispatcher
#
# paul cannon <pcannon@novell.com>
# mar 2005

"""A way to make generic functions -- functions with different versions
depending on the types of the incoming arguments. Used with decorators.

The best way to explain is probably by example, so:

    #!/usr/bin/env python2.4

    from dispatcher import *

    # This is made the default.
    @generic
    def foo(*args):
        print "foo%s" % str(args)

    # This is called when foo gets two arguments of type string.
    @foo.with_types(str, str)
    def bar(one, two):
        print "bar(%s, %s)" % (one, two)

    # This is called when foo gets four arguments, and the first, second,
    # and fourth are 29, 13, and 14, respectively. The third can be anything.
    @foo.with_values(29, 13, Any, 14)
    def baz(one, two, three, four):
        print "baz(%s, %s, %s, %s)" % (one, two, three, four)

    # This is called when foo gets 2 or more arguments, and the first and
    # last are of type int. ("Others" means any number of arguments of
    # any type and value).
    @foo.with_types(int, Others, int)
    def bonk(*args):
        print "bonk%s" % str(args)

    # This is called when the arguments to foo match these conditions: the
    # first is of type str, and the second is between 55 and 75.
    @foo.with_conditions(TypeMatch(str), lambda arg: 55 < arg < 75)
    def boo(*args):
        print "boo%s" % str(args)

    print "---------------------"
    print "this should call foo:"
    foo('hello', 12)

    print "---------------------"
    print "this should call bar:"
    foo('hello', 'world')

    print "---------------------"
    print "this should call baz:"
    foo(29, 13, {'big': 'extra', 'arg': 'in here'}, 14)

    print "---------------------"
    print "this should call bonk:"
    foo(29, 13, 14, 'what a world', 12)

    print "---------------------"
    print "this should call bonk:"
    foo(29, 12)

    print "---------------------"
    print "this should call foo:"
    foo('blob', 55)

    print "---------------------"
    print "this should call boo:"
    foo('blob', 56)

So the process is: decorate the default version of the function with
@dispatcher.generic. After that, you can use several attributes of
that function in order to add other possibilities to the generic
function: with_types, with_values, and with_conditions.

Each function can still be called normally after becoming a candidate
for the generic function.

Note that this causes some confusion when used with methods of a class:
when normally bound, the 'self' argument is automatically inserted, so
your with_* decorators need to allow for that argument. If you don't
want to need to deal with that, use the @dispatcher.genericmethod
decorator instead. This will cause the first parameter for each call
to be treated specially- it won't be compared against any of the items
in the specified argument values, types, etc.

"""

class Any:
    """Pass this as an argument to a generic function's with_* decorators
    to indicate that "Any" one argument can match in that position.
    """

class Others:
    """Pass this as an argument to a generic function's with_* decorators
    to indicate that any number of arguments of any sort (or zero arguments)
    can match in that position.
    """