[Twisted-web] more nevow patches

James Y Knight twisted-web@twistedmatrix.com
Fri, 16 Jan 2004 18:12:42 -0500


--Apple-Mail-3--649201377
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=US-ASCII;
	format=flowed

This time we've got the following:
1) context.patterns: replaced 'raiseIfMissing' argument with 'default' 
argument, as it's more flexible. Had to move 'invisible' into stan.py.
2) renderer.flatten: made it smaller, faster, better.
3) renderer.HTMLRenderer: class/initializer attribute 
"beExtremelyLenient" that gets passed to microdom, so that even sucky 
HTML can be used as a template, if desired. Defaults to false, of 
course.
5) stan.Proto has a clone method that returns self.
4) replaced ISerializable adapter with a simple function call. 
Currently looks up the function to call in the adapter binding list, 
which is kinda a hack, but simplest for now.
ISerializable(obj).serialize(obj, context, stream) is now 
renderer.serialize(obj, context).

The serialize and flatten changes together seriously sped up my app.

James


--Apple-Mail-3--649201377
Content-Transfer-Encoding: 7bit
Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name="nevow2.patch"
Content-Disposition: attachment;
	filename=nevow2.patch

Index: context.py
===================================================================
RCS file: /cvs/Quotient/nevow/context.py,v
retrieving revision 1.15
diff -u -r1.15 context.py
--- context.py	16 Jan 2004 21:36:27 -0000	1.15
+++ context.py	16 Jan 2004 23:08:34 -0000
@@ -173,13 +173,17 @@
                 return
         top.parent = context
 
-    def patterns(self, pattern, raiseIfMissing=True):
+    def patterns(self, pattern, default=None):
         """Generate clones of pattern tags forever, looping around to the beginning
         when we run out of unique matches.
+
+        If no matches are found, and default is None, raise an exception,
+        otherwise, return clones of default, forever.
+
         """
         tag = self.tag.clone()
         stripContexts(tag)
-        patterner = self._locatePatterns(tag, pattern, raiseIfMissing)
+        patterner = self._locatePatterns(tag, pattern, default)
         return PatternTag(patterner)
 
     def slotted(self, slot):
@@ -198,7 +202,7 @@
         warnings.warn("use patterns instead", stacklevel=2)
         return self.patterns(pattern)
 
-    def _locatePatterns(self, tag, pattern, raiseIfMissing):
+    def _locatePatterns(self, tag, pattern, default):
         keeplooking = True
         while keeplooking:
             keeplooking = False
@@ -207,10 +211,10 @@
                 cloned = x.clone()
                 cloned.pattern = Unset
                 yield cloned
-        if raiseIfMissing:
+        if default is None:
             raise RuntimeError, "Pattern %s was not found." % pattern
         while True:
-            yield invisible
+            yield default.clone()
 
     def _locateOne(self, name, locator, descr):
         found = False
Index: freeform.py
===================================================================
RCS file: /cvs/Quotient/nevow/freeform.py,v
retrieving revision 1.88
diff -u -r1.88 freeform.py
--- freeform.py	16 Jan 2004 21:36:27 -0000	1.88
+++ freeform.py	16 Jan 2004 23:08:34 -0000
@@ -446,7 +446,7 @@
                         ]
                     else:
                         ## No configurable, let's just throw whatever it was in the dom
-                        if iwoven.ISerializable(value, None):
+                        if renderer.getSerializer(value):
                             try:
                                 for x in range(100):
                                     theData = context.locate(iwoven.IData, x+1)
@@ -529,7 +529,7 @@
         if getattr(typedRenderer, 'complexType', False):
             return invisible(data=data, renderer=typedRenderer)
 
-        flat = flatten(iwoven.ISerializable(invisible(data=data, renderer=typedRenderer, key=data.name)).serialize(context, None))
+        flat = flatten(renderer.serialize(invisible(data=data, renderer=typedRenderer, key=data.name), context))
         if not flat:
             return ''
         if isGrouped(context, 2):
Index: iwoven.py
===================================================================
RCS file: /cvs/Quotient/nevow/iwoven.py,v
retrieving revision 1.9
diff -u -r1.9 iwoven.py
--- iwoven.py	13 Jan 2004 18:26:12 -0000	1.9
+++ iwoven.py	16 Jan 2004 23:08:34 -0000
@@ -62,14 +62,13 @@
     """A web request
     """
 
-
 class ISerializable(components.Interface):
-    def serialize(self, context, stream):
-        """Serialize the adaptee to the given stream, with the given context
+    ## fixme -- how to describe new interface?
+    def serialize(self, context):
+        """Serialize the adaptee, with the given context
         stack if necessary.
         """
 
-
 class IStatusMessage(components.Interface):
     """A marker interface, which should be set on the user's web session
     to an object which can be cast to a string, which will be shown to the
Index: renderer.py
===================================================================
RCS file: /cvs/Quotient/nevow/renderer.py,v
retrieving revision 1.32
diff -u -r1.32 renderer.py
--- renderer.py	16 Jan 2004 21:36:27 -0000	1.32
+++ renderer.py	16 Jan 2004 23:08:34 -0000
@@ -25,39 +25,66 @@
 from nevow import tags
 from nevow.context import WovenContext
 
+from twisted.python import components
+# FIXME: hack!
+# and the way serialize is implemented is possibly an
+# abuse of the components system, but hey, it WORKS. :)
+
+cachedAdapters = {}
+def getSerializer(obj):
+    registry = components.getRegistry(None)
+    
+    if hasattr(obj, '__class__'):
+        klas = obj.__class__
+    else:
+        klas = type(obj)
+
+    adapter = cachedAdapters.get(klas, None)
+    if adapter is not None:
+        return adapter
+    
+    # print "Adding cache entry for ",klas
+    fromInterfaces = components.classToInterfaces(klas)
+    for fromInterface in fromInterfaces:
+        # print " trying: ", fromInterface
+        adapter = registry.getAdapterFactory(fromInterface, ISerializable, None)
+        if adapter is not None:
+            cachedAdapters[klas] = adapter
+            return adapter
+    return None
+
+def serialize(obj, context):
+    serializer = getSerializer(obj)
+    if serializer is not None:
+        return serializer(obj, context)
+    raise NotImplementedError('%s instance (type %s) does not implement %s, and '
+                              'there is no registered adapter.' %
+                              (obj, type(obj), ISerializable))
 
-def flatten(gen):
-    """
-    I am a permissive flattener for precompilation.
-    """
-    results = []
-    accumulator = ''
+
+def _flatten(gen, straccum, results):
     for item in gen:
         if isinstance(item, types.StringTypes):
-            accumulator += item
+            straccum.append(item)
         else:
-            if isinstance(item, types.GeneratorType):
-                for sub in flatten(item):
-                    if isinstance(sub, types.StringTypes):
-                        accumulator += sub
-                    else:
-                        if accumulator:
-                            results.append(xml(accumulator))
-                        results.append(sub)
-                        accumulator = ''
-            elif isinstance(item, types.ListType):
-                 if accumulator:
-                     results.append(xml(accumulator))
-                 results.extend(item)
-                 accumulator=''
+            if isinstance(item, (types.GeneratorType, types.ListType)):
+                _flatten(item, straccum, results)
             else:
-                # anything that can be adapted to ISerializable
-                if accumulator:
-                    results.append(xml(accumulator))
+                # anything that can be serialize()d
+                if straccum:
+                    results.append(xml(''.join(straccum)))
                 results.append(item)
-                accumulator = ''
-    if accumulator:
-        results.append(xml(accumulator))
+                del straccum[:]
+    
+def flatten(gen):
+    """
+    I am a permissive flattener for precompilation.
+    """
+    straccum = []
+    results = []
+    _flatten(gen, straccum, results)
+    if straccum:
+        results.append(xml(''.join(straccum)))
     return results
 
 def _strflatten(context, gen, buf):
@@ -73,7 +100,7 @@
                 print "ERROR: Deferred has no result!"
                 buf("ERROR: Deferred has no result!")
             else:
-                for sub in _strflatten(context, ISerializable(item.result).serialize(context, None), buf):
+                for sub in _strflatten(context, serialize(item.result, context), buf):
                     yield sub
         else:
             for sub in _strflatten(context, item, buf):
@@ -215,7 +242,7 @@
         context = WovenContext(precompile=True)
         context.remember(self, resource.IResource)
         context.remember(self, IRendererFactory)
-        _documents[klsnm] = rv = flatten(ISerializable(self.document).serialize(context, None))
+        _documents[klsnm] = rv = flatten(serialize(self.document, context))
         return rv
 
     beforeRender = None
@@ -258,7 +285,7 @@
             def finisher():
                 finishRequest()
                 return request.finish()
-        render(ISerializable(self.doc).serialize(context, request), context, writer, finisher)
+        render(serialize(self.doc, context), context, writer, finisher)
         return server.NOT_DONE_YET
 
 
@@ -268,21 +295,24 @@
     """
     templateDirectory = ''
     templateFile = ''
+    beExtremelyLenient=False
     key = 'content'
-    def __init__(self, original=None, templateDirectory=None, templateFile=None, key=None):
+    def __init__(self, original=None, templateDirectory=None, templateFile=None, key=None, beExtremelyLenient=None):
         if templateDirectory is not None:
             self.templateDirectory = templateDirectory
         if templateFile is not None:
             self.templateFile = templateFile
         if key is not None:
             self.key = key
+        if beExtremelyLenient is not None:
+            self.beExtremelyLenient = beExtremelyLenient
         self.precompileTime = None
         Renderer.__init__(self, original)
 
     def precompile(self):
         context = WovenContext(precompile=True).remember(self, resource.IResource)
-        dom = microdom.parse(os.path.join(self.templateDirectory, self.templateFile))
-        doc = flatten(ISerializable(dom).serialize(context, None))
+        dom = microdom.parse(os.path.join(self.templateDirectory, self.templateFile), beExtremelyLenient=self.beExtremelyLenient)
+        doc = flatten(serialize(dom, context))
         # Precompiled. Record the time so we know when to reload the template.
         self.precompileTime = time.time()
         return doc
@@ -325,7 +355,7 @@
     def __init__(self, doc):
         context = WovenContext(precompile=True).remember(self, resource.IResource)
         context.remember(self, IRendererFactory)
-        self.doc = flatten(ISerializable(doc).serialize(context, None))
+        self.doc = flatten(serialize(doc, context))
         ## Bypass Renderer init
         self.toremember = []
         resource.Resource.__init__(self)
Index: stan.py
===================================================================
RCS file: /cvs/Quotient/nevow/stan.py,v
retrieving revision 1.17
diff -u -r1.17 stan.py
--- stan.py	16 Jan 2004 21:36:27 -0000	1.17
+++ stan.py	16 Jan 2004 23:08:34 -0000
@@ -19,6 +19,8 @@
     def __getitem__(self, children):
         return Tag(self)[children]
 
+    def clone(self, deep=True):
+        return self
 
 class xml(str):
     """Raw xml marker
@@ -220,7 +222,7 @@
 def sequence(context, data):
     headers = specialMatches(context.tag, 'pattern', 'header')
     pattern = context.patterns('item')
-    divider = context.patterns('divider', raiseIfMissing=False)
+    divider = context.patterns('divider', default=Proto(''))
     content = [(pattern(data=element), divider(data=element)) for element in data]
     if not content:
         content = specialMatches(context.tag, 'pattern', 'empty')
@@ -251,5 +253,7 @@
     def __call__(self, **kw):
         return NotImplementedError('comments are not callable')
 
+invisible = Proto('')
+
 
 
Index: tags.py
===================================================================
RCS file: /cvs/Quotient/nevow/tags.py,v
retrieving revision 1.8
diff -u -r1.8 tags.py
--- tags.py	13 Jan 2004 01:46:40 -0000	1.8
+++ tags.py	16 Jan 2004 23:08:34 -0000
@@ -4,11 +4,9 @@
 # Public License as published by the Free Software Foundation.
 
 
-from nevow.stan import Proto, Tag, directive, xml, CommentProto
+from nevow.stan import Proto, Tag, directive, xml, CommentProto, invisible
 
 
-invisible = Proto('')
-
 comment = CommentProto()
 
 tags = [
Index: url.py
===================================================================
RCS file: /cvs/Quotient/nevow/url.py,v
retrieving revision 1.6
diff -u -r1.6 url.py
--- url.py	16 Jan 2004 21:36:27 -0000	1.6
+++ url.py	16 Jan 2004 23:08:34 -0000
@@ -64,19 +64,15 @@
 root = URLOverlay(rootaccessor)
 
 
-class URLOverlaySerializer(components.Adapter):
-    __implements__ = iwoven.ISerializable,
-
-    def serialize(self, context, stream):
-        over = self.original
-        if context.precompile:
-            yield self
-        else:
-            url = over.urlaccessor(context)
-            for (cmd, args, kw) in over.dolater:
-                url = getattr(url, cmd)(*args, **kw)
-            url.query = urllib.urlencode(over.appendquery)
-            yield xml(str(url))
+def URLOverlaySerializer(original, context, stream):
+    if context.precompile:
+        yield self
+    else:
+        url = original.urlaccessor(context)
+        for (cmd, args, kw) in original.dolater:
+            url = getattr(url, cmd)(*args, **kw)
+        url.query = urllib.urlencode(original.appendquery)
+        yield xml(str(url))
 
 
 ## This is totally unfinished and doesn't work yet.
Index: serial/flatmdom.py
===================================================================
RCS file: /cvs/Quotient/nevow/serial/flatmdom.py,v
retrieving revision 1.12
diff -u -r1.12 flatmdom.py
--- serial/flatmdom.py	16 Jan 2004 21:36:27 -0000	1.12
+++ serial/flatmdom.py	16 Jan 2004 23:08:34 -0000
@@ -5,38 +5,27 @@
 
 from __future__ import generators
 
-from nevow.iwoven import ISerializable
+from nevow.renderer import serialize
 from nevow.stan import Tag, xml, directive
 
 from twisted.python import components
 
-class MicroDomTextSerializer(components.Adapter):
-    __implements__ = ISerializable,
-    
-    def serialize(self, context, stream):
-        if self.original.raw:
-            yield self.original.nodeValue
-        else:
-            from twisted.xish.domish import escapeToXml
-            yield escapeToXml(self.original.nodeValue)
+def MicroDomTextSerializer(original, context):
+    if original.raw:
+        yield original.nodeValue
+    else:
+        from twisted.xish.domish import escapeToXml
+        yield escapeToXml(original.nodeValue)
 
 
-class MicroDomCommentSerializer(components.Adapter):
-    __implements__ = ISerializable,
+def MicroDomCommentSerializer(original, context):
+    yield xml("<!--%s-->" % original.data)
     
-    def serialize(self, context, stream):
-        yield xml("<!--%s-->" % self.original.data)
-
-class MicroDomEntityReferenceSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        yield xml(self.original.nodeValue)
-
+def MicroDomEntityReferenceSerializer(original, context):
+    yield xml(original.nodeValue)
 
-class MicroDomElementSerializer(components.Adapter):
-    __implements__ = ISerializable,
 
+def MicroDomElementSerializer(original, context):
     directiveMapping = {
         'render': 'renderer',
         'data': 'data',
@@ -46,43 +35,39 @@
         'pattern', 'slot', 'macro', 'fill-slot', 'key',
     ]
 
-    def serialize(self, context, stream):
-        element = self.original
-        attrs = dict(element.attributes) # get rid of CaseInsensitiveDict
-        specials = {}
-        attributes = self.attributeList
-        directives = self.directiveMapping
-        for k, v in attrs.items():
-            # I know, this is totally not the way to do xml namespaces but who cares right now
-            ## I'll fix it later
-            if not k.startswith('nevow:'):
-                continue
-            _, nons = k.split(':')
-            if nons in directives:
-                ## clean this up by making the names more consistent
-                specials[directives[nons]] = directive(v)
-                del attrs[k]
-            if nons in attributes:
-                specials[nons] = v
-                del attrs[k]
-
-        yield ISerializable(
-            Tag(
-                element.tagName,
-                attributes=attrs,
-                children=element.childNodes,
-                specials=specials
-            )
-        ).serialize(context, stream)
-
-
-class MicroDomDocumentSerializer(components.Adapter):
-    __implemenents__ = ISerializable,
-
-    def serialize(self, context, stream):
-        if self.original.doctype:
-            yield "<!DOCTYPE %s!>\n" % self.original.doctype
-        for n in self.original.childNodes:
-            yield ISerializable(n).serialize(context, stream)
+    element = original
+    attrs = dict(element.attributes) # get rid of CaseInsensitiveDict
+    specials = {}
+    attributes = attributeList
+    directives = directiveMapping
+    for k, v in attrs.items():
+        # I know, this is totally not the way to do xml namespaces but who cares right now
+        ## I'll fix it later
+        if not k.startswith('nevow:'):
+            continue
+        _, nons = k.split(':')
+        if nons in directives:
+            ## clean this up by making the names more consistent
+            specials[directives[nons]] = directive(v)
+            del attrs[k]
+        if nons in attributes:
+            specials[nons] = v
+            del attrs[k]
+
+    yield serialize(
+        Tag(
+            element.tagName,
+            attributes=attrs,
+            children=element.childNodes,
+            specials=specials
+        ),
+        context)
+
+
+def MicroDomDocumentSerializer(original, context):
+    if original.doctype:
+        yield "<!DOCTYPE %s!>\n" % original.doctype
+    for n in original.childNodes:
+        yield serialize(n, context)
 
 
Index: serial/flatstan.py
===================================================================
RCS file: /cvs/Quotient/nevow/serial/flatstan.py,v
retrieving revision 1.20
diff -u -r1.20 flatstan.py
--- serial/flatstan.py	16 Jan 2004 21:36:27 -0000	1.20
+++ serial/flatstan.py	16 Jan 2004 23:08:34 -0000
@@ -9,217 +9,181 @@
 import warnings
 
 from twisted.python import components, log
+from twisted.internet import defer
 from nevow.stan import Proto, Tag, xml, directive
-from nevow.iwoven import ISerializable, IRendererFactory, IData
-from nevow.renderer import flatten
+from nevow.iwoven import IRendererFactory, IData
+from nevow.renderer import flatten, serialize
 from nevow.accessors import convertToData
 
-class ProtoSerializer(components.Adapter):
-    __implements__ = ISerializable,
+def ProtoSerializer(original, context):
+    yield xml('<%s />' % original)
 
-    def serialize(self, context, stream):
-        yield xml('<%s />' % self.original)
 
-
-class TagSerializer(components.Adapter):
-    __implements__ = ISerializable,
-    
-    def serialize(self, context, stream):
-        visible = bool(self.original.tagName)
-        singleton = not self.original.renderer and not self.original.children and not self.original.data
-        special = context.precompile and self.original._specials
-        if self.original.renderer:
-            ## If we have a renderer function we want to render what it returns, not our tag
-            visible = False
-        if special:
-            context = context.with(self.original)
-            context.tag.children = flatten(ISerializable(context.tag.children).serialize(context, stream))
-            yield context
+def TagSerializer(original, context):
+    visible = bool(original.tagName)
+    singleton = not original.renderer and not original.children and not original.data
+    special = context.precompile and original._specials
+    if original.renderer:
+        ## If we have a renderer function we want to render what it returns, not our tag
+        visible = False
+    if special:
+        context = context.with(original)
+        context.tag.children = flatten(serialize(context.tag.children, context))
+        yield context
+    else:
+        if visible:
+            yield xml('<%s' % original.tagName)
+            if original.attributes:
+                for (k, v) in original.attributes.items():
+                    if v is None:
+#                            warnings.warn("An attribute value for key %r on tag %r was None; ignoring attribute" % (original.tagName, v))
+                        continue
+                    yield xml(' %s="' % k)
+                    if context.precompile:
+                        yield v
+                    else:
+                        flat = flatten(serialize(v,context))
+                        if flat:
+                            val = flat[0]
+                            if isinstance(val, StringTypes):
+                                val = val.replace('"', '&quot;')
+                            yield xml(val)
+                    yield xml('"')
+        if singleton:
+            if visible:
+                yield xml(' />')
         else:
             if visible:
-                yield xml('<%s' % self.original.tagName)
-                if self.original.attributes:
-                    for (k, v) in self.original.attributes.items():
-                        if v is None:
-                            warnings.warn("An attribute value for key %r on tag %r was None; ignoring attribute" % (self.original.tagName, v))
-                            continue
-                        yield xml(' ')
-                        yield xml('%s="' % k)
-                        if context.precompile:
-                            yield v
-                        else:
-                            flat = flatten(ISerializable(v).serialize(context, stream))
-                            if flat:
-                                val = flat[0]
-                                if isinstance(val, StringTypes):
-                                    val = val.replace('"', '&quot;')
-                                yield xml(val)
-                        yield xml('"')
-            if singleton:
-                if visible:
-                    yield xml(' />')
-            else:
-                if visible:
-                    yield xml('>')
-                # TODO: Make this less buggy.
-                try:
-                    if context.locate(IData) != self.original.data:
-                        context = context.with(self.original)
-                except KeyError:
-                    context = context.with(self.original)
-                if self.original.renderer:
-                    toBeRenderedBy = self.original.renderer
-                    self.original.renderer = None
-                    yield ISerializable(toBeRenderedBy, persist=False).serialize(context, stream)
-                    self.original.wasRenderedBy = toBeRenderedBy
-                elif self.original.children:
-                    for child in self.original.children:
-                        yield ISerializable(child).serialize(context, stream)
-                if visible:
-                    yield xml('</')
-                    yield xml(self.original.tagName)
-                    yield xml('>')
-
-
-class StringSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        from twisted.xish.domish import escapeToXml
-        ## quote it
-        yield escapeToXml(self.original)
-
-
-class NoneWarningSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        yield xml('<span style="position: relative; font-size: 100; font-weight: bold; color: red; border: thick solid red;">None</span>')
-
-
-class StringCastSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        from twisted.xish.domish import escapeToXml
-        ## quote it
-        return escapeToXml(str(self.original))
-
-
-class ListSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        for item in self.original:
-            yield ISerializable(item).serialize(context, stream)
+                yield xml('>')
+            # TODO: Make this less buggy.
+            try:
+                if context.locate(IData) != original.data:
+                    context = context.with(original)
+            except KeyError:
+                context = context.with(original)
+            except TypeError:
+                context = context.with(original)
+            if original.renderer:
+                toBeRenderedBy = original.renderer
+                original.renderer = None
+                yield serialize(toBeRenderedBy,context)
+                original.wasRenderedBy = toBeRenderedBy
+            elif original.children:
+                for child in original.children:
+                    yield serialize(child, context)
+            if visible:
+                yield xml('</%s>' % original.tagName)
 
+def StringSerializer(original, context):
+    from twisted.xish.domish import escapeToXml
+    ## quote it
+    yield escapeToXml(original)
 
-class XmlSerializer(components.Adapter):
-    __implements__ = ISerializable,
 
-    def serialize(self, context, stream):
-        return self.original
+def NoneWarningSerializer(original, context):
+    yield xml('<span style="position: relative; font-size: 100; font-weight: bold; color: red; border: thick solid red;">None</span>')
 
 
-PASS_SELF = object()
+def StringCastSerializer(original, context):
+    from twisted.xish.domish import escapeToXml
+    ## quote it
+    return escapeToXml(str(original))
 
 
-class FunctionSerializer(components.Adapter):
-    __implements__ = ISerializable,
+def ListSerializer(original, context):
+    for item in original:
+        yield serialize(item, context)
 
-    def nocontext(self):
-        code = getattr(self.original, 'func_code', None)
-        if code is None:
-            return True
-        argcount = code.co_argcount
-        if argcount == 1:
-            return True
-        if argcount == 3:
-            return PASS_SELF
-        return False
-
-    def serialize(self, context, stream):
-        if context.precompile:
-            yield self.original
-        else:
-            data = convertToData(context, context.locate(IData))
-            try:
-                nocontext = self.nocontext()
-                if nocontext is True:
-                    result = self.original(data)
+
+def XmlSerializer(original, context):
+    return original
+
+
+PASS_SELF = object()
+
+
+def FunctionSerializer_nocontext(original):
+    code = getattr(original, 'func_code', None)
+    if code is None:
+        return True
+    argcount = code.co_argcount
+    if argcount == 1:
+        return True
+    if argcount == 3:
+        return PASS_SELF
+    return False
+
+def FunctionSerializer(original, context, nocontextfun=FunctionSerializer_nocontext):
+    if context.precompile:
+        yield original
+    else:
+        data = convertToData(context, context.locate(IData))
+        try:
+            nocontext = nocontextfun(original)
+            if nocontext is True:
+                result = original(data)
+            else:
+                if nocontext is PASS_SELF:
+                    renderFactory = context.locate(IRendererFactory)
+                    result = original(renderFactory, context, data)
                 else:
-                    if nocontext is PASS_SELF:
-                        renderFactory = context.locate(IRendererFactory)
-                        result = self.original(renderFactory, context, data)
-                    else:
-                        result = self.original(context, data)
-            except StopIteration:
-                log.err()
-                raise RuntimeError, "User function %r raised StopIteration." % self.original
-            yield ISerializable(result).serialize(context, stream)
+                    result = original(context, data)
+        except StopIteration:
+            log.err()
+            raise RuntimeError, "User function %r raised StopIteration." % original
+        yield serialize(result, context)
 
 
-class DeferredSerializer(components.Adapter):
-    def serialize(self, context, stream):
-        yield self.original
+def DeferredSerializer(original, context):
+    yield original
 
 
-class MethodSerializer(FunctionSerializer):
-    def nocontext(self):
-        func = getattr(self.original, 'im_func', None)
+def MethodSerializer(original, context):
+    def nocontext(original):
+        func = getattr(original, 'im_func', None)
         code = getattr(func, 'func_code', None)
         return code is None or code.co_argcount == 2
+    return FunctionSerializer(original, context, nocontext)
 
 
-class CallableInstanceSerializer(FunctionSerializer):
-    def nocontext(self):
-        func = getattr(self.original.__call__, 'im_func', None)
+def CallableInstanceSerializer(original, context):
+    def nocontext(original):
+        func = getattr(original.__call__, 'im_func', None)
         code = getattr(func, 'func_code', None)
         return code is None or code.co_argcount == 2
+    return FunctionSerializer(original, context, nocontext)
 
-
-class DirectiveSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        rendererFactory = context.locate(IRendererFactory)
-        renderer = rendererFactory.renderer(context, self.original)
-        return ISerializable(renderer).serialize(context, stream)
-
-
-class ContextSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        originalContext = self.original.clone()
-        originalContext.precompile = context.precompile
-        originalContext.chain(context)
-        try:
-            yield flatten(ISerializable(originalContext.tag).serialize(originalContext, stream))
-        except:
-            from twisted.web import util
-            from twisted.python import failure
-            from twisted.internet import reactor, defer
-            d = defer.Deferred()
-            fail = failure.Failure()
-            reactor.callLater(0, lambda: d.callback(xml(util.formatFailure(fail))))
-            desc = str(fail.value)
-            yield ISerializable([
-                xml("""<div style="border: 1px dashed red; color: red; clear: both" onclick="this.childNodes[1].style.display = this.childNodes[1].style.display == 'none' ? 'block': 'none'">"""),
-                desc,
-                xml('<div style="display: none">'),
-                d,
-                xml('</div></div>')
-            ]).serialize(context, stream)
-
-
-class CommentSerializer(components.Adapter):
-    __implements__ = ISerializable,
-
-    def serialize(self, context, stream):
-        yield xml("<!--")
-        for x in self.original.children:
-            yield ISerializable(x).serialize(context, stream)
-        yield("-->")
-
-
+def DirectiveSerializer(original, context):
+    rendererFactory = context.locate(IRendererFactory)
+    renderer = rendererFactory.renderer(context, original)
+    return serialize(renderer, context)
+
+
+def ContextSerializer(original, context):
+    originalContext = original.clone()
+    originalContext.precompile = context and context.precompile or False
+    originalContext.chain(context)
+    try:
+        yield flatten(serialize(originalContext.tag, originalContext))
+    except:
+        from twisted.web import util
+        from twisted.python import failure
+        from twisted.internet import reactor, defer
+        d = defer.Deferred()
+        fail = failure.Failure()
+        reactor.callLater(0, lambda: d.callback(xml(util.formatFailure(fail))))
+        desc = str(fail.value)
+        yield serialize([
+            xml("""<div style="border: 1px dashed red; color: red; clear: both" onclick="this.childNodes[1].style.display = this.childNodes[1].style.display == 'none' ? 'block': 'none'">"""),
+            desc,
+            xml('<div style="display: none">'),
+            d,
+            xml('</div></div>')
+        ], context)
+
+
+def CommentSerializer(self, context):
+    yield xml("<!--")
+    for x in self.original.children:
+        yield serialize(x, context)
+    yield("-->")
Index: test/test_flatstan.py
===================================================================
RCS file: /cvs/Quotient/nevow/test/test_flatstan.py,v
retrieving revision 1.16
diff -u -r1.16 test_flatstan.py
--- test/test_flatstan.py	16 Jan 2004 21:36:28 -0000	1.16
+++ test/test_flatstan.py	16 Jan 2004 23:08:34 -0000
@@ -12,8 +12,8 @@
 from nevow import stan
 from nevow import context
 from nevow import tags
-from nevow.iwoven import ISerializable, IData, IRendererFactory, IRequest
-from nevow.renderer import flatten, render
+from nevow.iwoven import IData, IRendererFactory, IRequest
+from nevow.renderer import flatten, render, serialize
 from nevow.util import FakeRequest
 
 from twisted.trial import unittest
@@ -39,8 +39,8 @@
         ctx = self.setupContext(precompile, setupRequest)
         ctx = setupContext(ctx)
         if precompile:
-            return flatten(ISerializable(tag).serialize(ctx, None))
-        rv = render(ISerializable(tag).serialize(ctx, None))
+            return flatten(serialize(tag, ctx))
+        rv = render(serialize(tag, ctx))
         if isinstance(rv, defer.Deferred):
             print "DEFERRED RENDER"
             return unittest.deferredResult(rv)

--Apple-Mail-3--649201377--