root/trunk/util.py

Revision 137 (checked in by johnny, 3 years ago)

Implemented basic customization of type handling - you can add ':iter', ':str'
or ':ctx' behind a content or replace key to force handling the value as an
iterable, string or context respectively (some behaviour needs to be defined,
still, but the basics are there).

Line 
1 # string conversion stuff
2 def entitize(s):
3     """ entitize a string so it can be used in XML content and attrs
4     """
5     return s.replace('&', '&').replace('"', '"').replace(
6                 '<', '&lt;').replace('>', '&gt;')
7
8 def strconvert(s, charset):
9     from templess import nodebase
10     if isinstance(s, nodebase):
11         return s
12     elif isinstance(s, xmlstring):
13         return unicode(s)
14     elif isinstance(s, str):
15         s = unicode(s, charset)
16     elif not isinstance(s, unicode):
17         s = str(s)
18     return entitize(s)
19
20 # helper functions
21 def is_iterable_not_string(v):
22     """ determine if something is iterable but not a string
23     """
24     from templess import elnode
25     for t in (str, unicode, xmlstring, objectcontext, elnode):
26         if isinstance(v, t):
27             return False
28     try:
29         iter(v)
30     except TypeError:
31         return False
32     else:
33         return True
34
35
36 # some helper classes for templess users
37 class xmlstring(unicode):
38     """ wrapper around unicode to mark a string as XML
39
40         when Templess encounters this object, it will not escape entities
41     """
42
43 def iterwrapper(iterable):
44     for i in iterable:
45         yield objectcontext(i)
46
47
48 class objectcontext(object):
49     """ wrapper around objects to allow Templess to traverse attributes
50
51         this is similar to the nastyness Zope exposes to its users in TAL,
52         when Templess retrieves a key from the context, if the context is
53         an objectcontext wrapping an object, instead of just doing a
54         __getitem__ the following will be done:
55
56         - first a __getitem__ is tried, then a __getattr_, if the value is
57           still not found a KeyError is raised
58
59         - before the value is returned, a check is done whether it's callable,
60           and if so it's called (without arguments)
61     """
62     # XXX do we want to allow arguments in something like "@key,key" form or
63     # something scary like that? this is nasty already anyway ;)
64     def __init__(self, o):
65         self.object = o
66
67     __marker__ = []
68     def __getitem__(self, name):
69         try:
70             ret = self.object[name]
71         except (TypeError, KeyError):
72             try:
73                 ret = self.object.__dict__[name]
74             except KeyError:
75                 try:
76                     ret = self.object.__class__.__dict__[name]
77                 except KeyError:
78                     raise KeyError, name
79         if callable(ret):
80             ret = ret()
81         return self.wrap(ret)
82
83     def __str__(self):
84         return getattr(self.object, '__str__', self.object.__repr__)()
85
86     def __repr__(self):
87         return '<objectcontext for "%s">' % (self.object.__class__.__name__,)
88
89     def wrap(self, obj):
90         if is_iterable_not_string(obj):
91             return iterwrapper(obj)
92         return objectcontext(obj)
93
94     def __hasattr__(self, name):
95         return hasattr(self.object, name)
Note: See TracBrowser for help on using the browser.