[Zope] dtml-in sort by generic function

Steve Spicklemire steve@spvi.com
Tue, 27 Mar 2001 19:03:30 -0500 (EST)


>>>>> "DM" == Dieter Maurer <dieter@handshake.de> writes:

    DM> To separate the direction is a good idea. I like it.  I am not
    DM> so sure about the '/' separators.  I expect, we will soon get
    DM> "/" separated object access paths.  Then, the use of "/" for a
    DM> completely different purpose may be confusing. Not sure, that
    DM> my "[...]" is better in this respect...

Zieve does this with ':' sorta like name="foo:int"

Another trick from Zieve is the use of comparison classes:

__doc__='''

Comparison class for sorting object based on runtime attributes...

Steve Spicklemire
Silicon Prairie Ventures Inc.
steve@spvi.com

'''

__version__='$Revision: 1.1.1.1 $'[11:-2]

import string
import traceback
import sys

class compClass:

    #
    # The idea here is to provide generic sorting...
    #

    def __init__(self, sortAttrs = None, raiseExceptions = 0, theClass = None, emptyLast = 1 ):

        """ set up sort attributes and type conversions...."""
    
        self.sortAttrs = []
        self.raiseExceptions = raiseExceptions
        self.emptyLast = emptyLast

        if sortAttrs:
            for theAttr in sortAttrs:
                unbound = 1
                if string.find(theAttr,':') != -1:
                    theAttr, theType = string.split(theAttr,':')
                    if self.typeFuncs.has_key(theType):
                        theTypeFunc = self.typeFuncs[theType]
                    elif hasattr(self, theType) and callable(getattr(self, theType)):
                        theTypeFunc = getattr(self, theType)
                    elif theClass and hasattr(theClass, theType) and callable(getattr(theClass, theType)):
                        unbound = 0
                        theTypeFunc = getattr(theClass, theType)
                    else:
                        theTypeFunc = None
                else:
                    theTypeFunc = None

                self.sortAttrs.append((theAttr, theTypeFunc, unbound))

    def toInt(self, thing):
        result = 0
        if type(thing) == type(''):
            try:
                result = string.atoi(thing)
            except:
                if self.raiseExceptions:
                    raise
        else:
            result = thing

        return result

    def toChar(self, thing):
        result = ''
        if type(thing) != type(''):
            result = str(thing)
        else:
            result = thing
        return result

    def doComp(self, a, b):

        if self.emptyLast: # force empty strings last...
            
            if type(a) == type('') and type(b) == type(''):
                if len(a) == 0 and len(b) != 0:
                    return 1
                elif len(b) == 0 and len(a) != 0:
                    return -1
        if a<b:
            return -1

        if a==b:
            return 0

        return 1

    def __call__(self, thingA, thingB):

        if not self.sortAttrs:
            return self.doComp(thingA, thingB)
        
        result = 0

        for theAttr, theFunc, unbound in self.sortAttrs:
            if hasattr(thingA, theAttr) and hasattr(thingB, theAttr):
                a = getattr(thingA, theAttr)
                b = getattr(thingB, theAttr)

                if theFunc is not None:
                    if unbound:
                        [a,b] = map(lambda o,s=self,t=theFunc:apply(t,(s,o)),[a,b])  # map type conversion onto elements
                    else:
                        [a,b] = map(lambda o,s=self,t=theFunc:apply(t,(o,)),[a,b])  # map type conversion onto elements

                result = self.doComp(a,b)

                if result != 0:
                    break

        return result

    typeFuncs = { 'int': toInt, 'char': toChar }  # type conversion map....

def test():

    #
    # test this sort thingy on some random class....
    #
    
    class foo:
        a = 0
        b = 0
        first = ''

        def __repr__(self):
            return '( a->%s, b->%s, first->%s, )[%s]' % ( `self.a`, `self.b`, self.first, id(self))

    x = foo()
    y = foo()
    z = foo()

    x.a = '30'
    x.b = 10
    x.first = 'c'

    y.a = '30'
    y.b = 100
    y.first = 'D'

    z.a = '200'
    z.b = 10
    z.first = 'E'

    ccab = compClass(['a:int','b:char'])
    ccba = compClass(['b:int','a:char'])
    ccup = compClass(['first:toUpper'])
    ccabr = compClass(['a:int','b:char'], 1)
    cc = compClass()

    list1 = [x,y,z]
    list2 = ['200','30','300']

    list1.sort(ccab)
    print list1
    
    list1.sort(ccba)
    print list1
    
    list2.sort(cc)
    print list2

    list1.sort(ccup)
    print list1
    
    z.a = 'foo'
    
    try:
        list1.sort(ccabr)
    except ValueError: 
        print "we got the expected exception"

    gotit = 0
    try:
        list1.sort(ccab)
    except:
        gotit = 1
    if gotit:
        print "dang we got the exception we wanted to miss.."
    else:
        print "OK.. no exception seen..."
        
    
    
if __name__ == '__main__':
    test()