QuackTemplate.Wrapper is a template helper class: it takes arbitrary Python objects and uses__gettitem__ so that the wrapped object can be passed into the standard Python string substitution mechanism. It can follow method calls, dict keys and list members (to some extent).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | """
A quick-and-dirty template helper, using Python's "duck"-typing approach.
Usage: Wrap an object in QuackTemplate.Wrapper, and use it with a normal
Python template, like so:
print "%(attribute)s" % Wrapper(myObj)
See __main__ below for test cases and examples.
NB: QuackTemplate can follow encapsulated objects, lists and dicts,
but it can only call methods WITH ZERO arguments.
Costas Malamas 2006.02.22
"""
import types
class Wrapper:
def __init__(self, obj):
self.obj = obj
def __repr__(self):
return str(self.obj)
def __getitem__(self, key):
#
# Null key, same as repr of self
#
if not key:
return str(self)
#
# If there is a dereference, split the call
#
rem = None
idx = None
if "." in key:
key, rem = key.split('.', 1)
if '#' in key:
key, idx = key.split('#', 1)
try:
result = getattr(self.obj, key)
except AttributeError:
#
# Allow for last-minute additions to wrappers; if a key doesn't
# exist in the wrapped object, first test ourselves, then delegate
#
if self.__dict__.get(key):
result = self.__dict__[key]
else:
try:
result = self.obj[key]
except:
raise Exception, "Key '%s' (remainder: '%s') not found in object of type %s!" %\
(key, rem, self.obj.__class__)
except Exception, mesg:
print "Wrapper %s with key %s and remainder %s" % (str(self), key, rem)
print mesg
raise Exception
#
# If calling a member instance, delegate to it!
#
if type(result) == types.InstanceType:
return Wrapper(result)[rem]
#
# A method could return either another object
# or a result, so delegate the interim result
#
elif type(result) == types.MethodType:
interim = result()
return Wrapper(interim)[rem]
elif idx and type(result) == types.ListType:
return Wrapper(result[int(idx)])[rem]
elif rem:
return Wrapper(result[rem])
return Wrapper(result)[rem]
if __name__ == "__main__":
class Test:
def __init__(self):
self.var = "a String!"
self.intvar = 20
self.data = [1, 2]
self.lut = {"key" : 999}
def get(self):
return "from get()!"
def factory(self):
return Test()
def __repr__(self):
return "A Test instance!"
z = Test()
z.child = Test()
z.child.child = Test()
z.child.children = [Test(), Test()]
wz = Wrapper(z)
zz = Wrapper(Test())
wz.whatever = Wrapper(zz)
print "String attribute: %(var)s" % wz
print "Integer attribute: %(intvar)s" % wz
print "Method call: %(get)s" % wz
print "Attribute of a child: %(child.var)s" % wz
print "Method of a grand-child: %(child.child.get)s" % wz
print "Method of a generated object: %(child.factory.get)s" % wz
print "Repr of a generated object: %(child.child)s" % wz
print "Item of an encaps'ed list object: %(child.data#1)s" % wz
print "Value of a generated object's dict: %(child.lut.key)s" % wz
print "Method of an encaps'ed obj in a list: %(child.children#1.get)s" % wz
print "Dynamic addition to a wrapper: %(whatever.get)s" % wz
x = {"one": Wrapper(Test()), "two" : 4}
wx = Wrapper(x)
print "Object method in a dict: %(one.get)s" % wx
print "Value in a dict: %(two)s" % wx
|
I've found that my Python-based templates keep growing and growing and at some point there's just too many things I'd like to pass in for template substitution. So, this light-weight wrapper is just a convenient intermediary for sending large objects into standard Python string substitution.
Great solution to something I often want to do. I often wish objects would work with string formats. I normally add __getitem__ to my own classes, but it is a nuisance overriding other people's classes just to make them work with string formats. Your class solves the problem nicely.