Logo Search packages:      
Sourcecode: whyteboard version File versions  Download package


00001 '''
Low level functions and classes related to callables. 

from inspect import getargspec, ismethod, isfunction, getmro

KWARG_TOPIC = 'msgTopic' # must NOT be changed
AUTO_ARG    = 'your listener wants topic name'

00012 def getModule(obj):
    '''Get the module in which an object was defined. Returns '__main__' 
    if no module defined (which usually indicates either a builtin, or
    a definition within main script). '''
    if hasattr(obj, '__module__'):
        module = obj.__module__
        module = '__main__'
    return module

00023 def getID(callable_):
    '''Get name and module name for a callable, ie function, bound 
    method or callable instance, by inspecting the callable. E.g. 
    getID(Foo.bar) returns ('Foo.bar', 'a.b') if Foo.bar was
    defined in module a.b. '''
    sc = callable_
    if ismethod(sc):
        module = getModule(sc.im_self)
        id = '%s.%s' % (sc.im_self.__class__.__name__, sc.im_func.func_name)
    elif isfunction(sc):
        module = getModule(sc)
        id = sc.__name__
    else: # must be a functor (instance of a class that has __call__ method)
        module = getModule(sc)
        id = sc.__class__.__name__
    return id, module

00042 def getRawFunction(callable_):
    '''Given a callable, return (offset, func) where func is the
    function corresponding to callable, and offset is 0 or 1 to 
    indicate whether the function's first argument is 'self' (1)
    or not (0).'''
    firstArg = 0
    if isfunction(callable_):
        #print 'Function', getID(callable_)
        func = callable_
    elif ismethod(callable_):
        func = callable_
        #print 'Method', getID(callable_)
        firstArg = 1  # don't care about the self arg
    elif hasattr(callable_, '__call__'):
        #print 'Functor', getID(callable_)
        func = callable_.__call__
        firstArg = 1  # don't care about the self arg
        msg = 'type %s not recognized' % type(callable_)
        raise ValueError(msg)
    return firstArg, func

00066 class ListenerInadequate(TypeError):
    Raised when an attempt is made to subscribe a listener to 
    a topic without satisfying the topic requirements.
    def __init__(self, msg, listener, *args):
        idStr, module = getID(listener)
        msg = 'Listener %s (module %s) inadequate: %s' % (idStr, module, msg)
        TypeError.__init__(self, msg)
        self.msg    = msg
        self.args   = args
        self.module = module
        self.idStr  = idStr
    def __str__(self):
        return self.msg

00085 class ArgsInfo:
    Represent the "signature" or protocol of a listener in the context of 
00091     def __init__(self, args, firstArgIdx, defaultVals, acceptsAllKwargs=False):
        '''Args is the complete set of arguments as obtained form inspect.getargspec().
        The firstArgIdx points to the first item in args that is of use, so it is typically
        0 if listener is a function, and 1 if listener is a method. After initialization,
        the self.args will contain subset of args without first firstArgIdx items, 
        the self.numRequired will indicate number of required arguments, and 
        self.wantsTopic will be True only if listener indicated it wanted the topic 
        object to be auto-passed to it in a pubsub.sendMessage().
        Note that args may be different upon return.'''
        self.allArgs = args
        self.numRequired = None
        self.wantsTopic = None
        self.acceptsAllKwargs = acceptsAllKwargs
        defaultVals = list(defaultVals or ())
        self.__cleanup(firstArgIdx, defaultVals)

    def getRequiredArgs(self):
        return tuple( self.allArgs[:self.numRequired] )
00111     def __cleanup(self, firstArgIdx, defaultVals):
        '''Removes unnecessary items from args and defaultVals. 
        Returns a pair (num, wantTopic) where num is how many
        items in args represent the required arguments, and 
        wantTopic is True if args/defaultVals satisfied the 
        "wantTopic" condition. '''
        args = self.allArgs
        del args[0:firstArgIdx] # does nothing if firstArgIdx == 0
        self.numRequired = len(args) - len(defaultVals)
        assert self.numRequired >= 0
        # if listener wants topic, remove that arg from args/defaultVals
        self.wantsTopic = False
        if defaultVals is not None:
            wantTopicIdx = self.__isTopicWanted(defaultVals)
            if wantTopicIdx >= self.numRequired:
                del args[wantTopicIdx]
                del defaultVals[wantTopicIdx - self.numRequired]
                self.wantsTopic = True        
00132     def __isTopicWanted(self, defaults):
        '''Does the listener want topic of message? Returns < 0 if not, 
        otherwise return index of topic kwarg within args.'''
        args = self.allArgs
        firstKwargIdx = len(args) - len(defaults)
            findTopicArg = args.index(KWARG_TOPIC, firstKwargIdx)
        except ValueError:
            return -1
        topicKwargIdx = findTopicArg - firstKwargIdx
        if defaults[topicKwargIdx] != AUTO_ARG:
            return -1
        return findTopicArg

00150 def getArgs(listener):
    '''Returns an instance of ArgsInfo for the given listener. '''
    # figure out what is the actual function object to inspect:
        firstArgIdx, func = getRawFunction(listener)
    except ValueError, exc:
        raise ListenerInadequate(str(exc), listener)

    (args, va, vkwa, defaultVals) = getargspec(func)
    return ArgsInfo(args, firstArgIdx, defaultVals, vkwa)

Generated by  Doxygen 1.6.0   Back to index