[Twisted-web] Slot/pattern/key changes

James Y Knight twisted-web@twistedmatrix.com
Tue, 10 Feb 2004 16:30:48 -0500


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

This is a set of *BACKWARDS-INCOMPATIBLE* changes that simplifies and 
clarifies the set of attributes nevow has for finding elements. Tests 
pass, and my app works (both after being updated slightly for the API 
changes).

There used to be 3 attributes you could put on elements to locate them: 
slot, pattern, and key. They all did similar but slightly different 
things.

- Filling slots with data is a useful concept but 'slot' shouldn' t be 
an attribute. A slot is something that gets replaced with the given 
data, and is thus better as a standalone element. It has been replaced 
with the following:
slot('slotname') in a stan tree, or <nevow:slot name="slotname" /> in 
an (X)HTML template.
context.slotted() (which could locate a slot) has been eliminated and 
replaced with context.fillSlots(slotname, stantree). Note that you 
cannot locate slots, you can just specify data for them to fill 
themselves with.

- Assigning unique keys is also a useful concept, but it doesn't 
require the "context.keyed()" method. That method has been eliminated. 
HTMLRenderer used it to find the key="content" in HTML files used as 
sub-renderers, now it uses pattern="content".

- Patterns remain as the only way to locate a sub-element of your 
renderer. There are now 3 methods to locate them, depending on what you 
want to do. patternGenerator() replaces patterns(), onePattern() 
semi-replaces keyed(), and allPatterns() replaces the direct use of 
specialMatches that the sequence renderer previous did. Please see the 
docs in context.py for these three methods for precise behavior.

So, in summary, to convert your old code to new code:
1) If you had "div(style="border:1px", slot="myslot")" replace it with 
div(style="border: 1px")[slot("myslot")].
2) If you had a template with "<div style="border:1px" 
nevow:slot="myslot" />" replace it with "<div 
style="border:1px"><nevow:slot name="myslot" /></div>
3) If you had "context.slotted("myslot").clear()[a(href="foo")["bar"]] 
replace it with context.fillSlot("myslot", a(href="foo")["bar"]]
4) If you used keyed(), convert key= to pattern= and keyed() to 
onePattern().
5) If you used patterns(), just rename it to patternGenerator()

Also included in this patch are doc cleanups, removing excess xml() 
wrappings, replacing yield by return when possible, and the ability to 
use the "<nevow:invisible>" element in a (x)html template file, with 
the same behavior as the invisible stan tag.

James

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

Index: components.wsv
===================================================================
RCS file: /cvs/Quotient/nevow/components.wsv,v
retrieving revision 1.26
diff -u -r1.26 components.wsv
--- components.wsv	6 Feb 2004 23:03:47 -0000	1.26
+++ components.wsv	10 Feb 2004 21:26:17 -0000
@@ -39,6 +39,7 @@
 nevow.serial.flatstan.MethodSerializer              types.MethodType                nevow.iwoven.ISerializable
 nevow.serial.flatstan.CallableInstanceSerializer    nevow.iwoven.IRendererFactory   nevow.iwoven.ISerializable
 nevow.serial.flatstan.DirectiveSerializer           nevow.stan.directive            nevow.iwoven.ISerializable
+nevow.serial.flatstan.SlotSerializer                nevow.stan.slot                 nevow.iwoven.ISerializable
 nevow.serial.flatstan.ContextSerializer             nevow.context.WovenContext      nevow.iwoven.ISerializable
 nevow.serial.flatstan.DeferredSerializer            twisted.internet.defer.Deferred nevow.iwoven.ISerializable
 
Index: context.py
===================================================================
RCS file: /cvs/Quotient/nevow/context.py,v
retrieving revision 1.17
diff -u -r1.17 context.py
--- context.py	6 Feb 2004 21:21:51 -0000	1.17
+++ context.py	10 Feb 2004 21:26:17 -0000
@@ -39,8 +39,9 @@
 
 class WovenContext(object):
     key = None
-    _remembrances = {}
+    _remembrances = None
     tag = None
+    _slotData = None
     def __init__(self, parent=None, tag=None, precompile=False, remembrances=None, key=None, isAttrib=False):
         self.tag = tag
         self.parent = parent
@@ -87,29 +88,16 @@
         return self
 
     def with(self, tag):
-        """Remember a few things in a new context stack entry.
+        """Create a new context stack entry for the specified tag.
         
         tag: the tag the new context stack entry will be associated with.
-        data: The data to pass to any rendering functions.
-        renderer: The rendering function to use to render this node.
-        observer: The observer function to notify when an
-            event occurs to this node.
         """
-#        data=None, renderer=None, observer=None, remembrances=None
 
         new = WovenContext(self, tag, self.precompile, key=tag.key, isAttrib=self.isAttrib)
         if tag.data is not Unset:
             new.remember(tag.data, IData)
         if tag.remember is not Unset:
             new.remember(tag.remember)
-#        if renderer is not None:
-#            # push a renderer onto the stack
-#            pass
-# woah this seems broken
-#            new.remember(renderer, IRendererFactory)
-#        if observer is not None:
-#            # push an observer onto the stack
-#            pass
         return new
 
     def locate(self, interface, depth=1):
@@ -155,28 +143,67 @@
         top.parent = context
 
     def patterns(self, pattern, default=None):
-        """Generate clones of pattern tags forever, looping around to the beginning
-        when we run out of unique matches.
-
+        warnings.warn("use patternGenerator instead", stacklevel=2)
+        return self.patternGenerator(pattern, default)
+    
+    def patternGenerator(self, pattern, default=None):
+        """Returns a psudeo-Tag which will generate clones of matching
+        pattern tags forever, looping around to the beginning when running
+        out of unique matches.
+        
         If no matches are found, and default is None, raise an exception,
-        otherwise, return clones of default, forever.
+        otherwise, generate clones of default forever.
 
+        You can use the normal stan syntax on the return value.
+        
+        Useful to find repeating pattern elements. Example rendering function:
+        def simpleSequence(context, data):
+          pattern = context.patternCloner('item')
+          return [pattern(data=element) for element in data]
         """
         patterner = self._locatePatterns(self.tag, pattern, default)
         return PatternTag(patterner)
 
-    def slotted(self, slot):
-        """Locate and return an existing slot in the current context tag, clearing any
-        dummy content in the process. You can then fill the slot with real content.
-        """
-        return self._locateOne(slot, self._locateSlots, 'slot')
-
-    def keyed(self, key):
-        """Locate an existing keyed node in the current context tag. This returns the node
-        unchanged, without clearing or cloning anything.
+    def allPatterns(self, pattern):
+        """Return a list of all matching pattern tags, not cloned.
+        
+        Useful if you just want to insert them in the output in one
+        place.
+        
+        E.g. the sequence renderer's header and footer are found with this.
         """
-        return self._locateOne(key, self._locateKeys, 'key')
+        return list(specialMatches(self.tag, 'pattern', pattern))
 
+    def onePattern(self, pattern):
+        """Return a single matching pattern, not cloned.
+        If there is more than one matching pattern or no matching patterns,
+        raise an exception.
+
+        Useful in the case where you want to locate one and only one
+        sub-tag and do something with it.
+        """
+        return self._locateOne(pattern,
+                               lambda pattern: specialMatches(self.tag, 'pattern', pattern),
+                               'pattern')
+
+    def fillSlots(self, name, stan):
+        """Set 'stan' as the stan tree to replace all slots with name 'name'.
+        """
+        if self._slotData is None:
+            self._slotData = {}
+        self._slotData[name] = stan
+
+    def _locateSlotData(self, name):
+        """Find previously remembered slot filler data.
+        For use by flatstan.SlotRenderer"""
+        if self._slotData:
+            data = self._slotData.get(name, Unset)
+            if data is not Unset:
+                return data
+        if self.parent is None:
+            raise KeyError, "Slot data %s was not remembered." % name
+        return self.parent._locateSlotData(name)
+    
     def _locatePatterns(self, tag, pattern, default):
         keeplooking = True
         gen = specialMatches(tag, 'pattern', pattern)
@@ -208,15 +235,6 @@
             raise NodeNotFound(descr, name)
         return found
 
-    def _locateSlots(self, slot):
-        for match in specialMatches(self.tag, 'slot', slot):
-            yield match.clear()
-
-    def _locateKeys(self, key):
-        for keySpecial in specials(self.tag, 'key'):
-            if keySpecial.key.endswith(key):
-                yield keySpecial        
-
     def clone(self, deep=True, cloneTags=True):
         ## don't clone the tags of parent contexts. I *hope* code won't be
         ## trying to modify parent tags so this should not be necessary.
Index: renderer.py
===================================================================
RCS file: /cvs/Quotient/nevow/renderer.py,v
retrieving revision 1.44
diff -u -r1.44 renderer.py
--- renderer.py	6 Feb 2004 21:21:51 -0000	1.44
+++ renderer.py	10 Feb 2004 21:26:17 -0000
@@ -397,7 +397,7 @@
     def __call__(self, context, data):
         self.check()
         ctx = context.with(tags.invisible[self.doc])
-        return ctx.keyed(self.key)
+        return ctx.allPatterns(self.key)
 
 
 class XMLRenderer(HTMLRenderer):
Index: stan.py
===================================================================
RCS file: /cvs/Quotient/nevow/stan.py,v
retrieving revision 1.20
diff -u -r1.20 stan.py
--- stan.py	6 Feb 2004 23:03:47 -0000	1.20
+++ stan.py	10 Feb 2004 21:26:17 -0000
@@ -37,6 +37,15 @@
     def __repr__(self):
         return "directive('%s')" % self.name
 
+class slot(object):
+    """Marker for slot insertion in a template
+    """
+    __slots__ = ['name']
+    def __init__(self, name):
+        self.name = name
+
+    def __repr__(self):
+        return "slot('%s')" % self.name
 
 class Tag(object):
     """Tag instances represent html tags with a tag name, attributes,
@@ -46,9 +55,7 @@
     which make representing trees of XML natural using pure python
     syntax. See the docstrings for these methods for more details.
     """
-    specials = ['data', 'renderer', 'observer', 'remember', 'pattern', 'slot', 'macro', 'fill-slot', 'key']
-
-    produceContext = False
+    specials = ['data', 'renderer', 'remember', 'pattern', 'key']
 
     def __init__(self, tag, attributes=None, children=None, specials=None):
         self.tagName = tag
@@ -70,20 +77,32 @@
         __call__ because it then allows the natural syntax:
         
         table(width="100%", height="50%", border="1")
+
+        Attributes may be 'invisible' tag instances (so that
+        a(href=invisible(data="foo", renderer=myhrefrenderer) works),
+        strings, functions, or any other object which has a registered
+        ISerializable adapter.
         
-        Three magic attributes may
-        have values other than string values, and will be remembered
-        in the context stack for later retrieval:
-        
-        data
-        renderer
-        observer
-
-        See WovenContext.with for details.
-        
-        pattern and slot attributes are special, and are not exported
-        to the final page, but are used to locate nodes tagged with
-        a given pattern or slot id.
+        A few magic attributes have values other than these, as they
+        are not serialized for output but rather have special purposes
+        of their own:
+        
+        data - The value is saved on the context stack and passed to
+               renderer functions.
+        renderer - A function to call that may modify the tag in any
+                   way desired.
+        remember - Remember the value on the context stack with
+                   context.remember(value) for later lookup with
+                   context.locate()
+        pattern - Value should be a key that can later be used to locate
+                  this tag with context.patternGenerator() or
+                  context.allPatterns()
+        key - A string used to give the node a unique label.
+              This is automatically namespaced, so in:
+              span(key="foo")[span(key="bar")]
+              the inner span actually has a key of 'foo.bar'.
+              The key is is intended for use as e.g. an html 'id' attribute,
+              but will is not automatically output.
         """
         if not kw:
             return self
@@ -101,7 +120,7 @@
     def __getitem__(self, children):
         """Add children to this tag. Multiple children may be added by
         passing a tuple or a list. Children may be other tag instances,
-        strings, functions, or any object which has a registered
+        strings, functions, or any other object which has a registered
         ISerializable adapter.
         
         This is implemented using __getitem__ because it then allows
@@ -129,6 +148,18 @@
         """
         raise NotImplementedError, "Stan tag instances are not iterable."
 
+    def precompilable(self):
+        """Is this tag precompilable?
+        
+        Tags are precompilable if they will not be modified by a user
+        renderer function.
+
+        Currently, the following attributes prevent the tag from being
+        precompiled:
+        - renderer (because the function can modify its own tag)
+        - pattern (not sure about this, but perhaps may get modified)
+        """
+        
     def _clone(self, obj, deep):
         if hasattr(obj, 'clone'):
             return obj.clone(deep)
@@ -170,7 +201,6 @@
             rstr += ', children=%r' % self.children
         return "Tag(%r%s)" % (self.tagName, rstr)
 
-
 class UnsetClass:
     def __nonzero__(self):
         return False
@@ -222,26 +252,23 @@
                 yield match
 
 def sequence(context, data):
-    headers = specialMatches(context.tag, 'pattern', 'header')
-    pattern = context.patterns('item')
-    divider = context.patterns('divider', default=Proto(''))
+    headers = context.allPatterns('header')
+    pattern = context.patternGenerator('item')
+    divider = context.patternGenerator('divider', default=invisible)
     content = [(pattern(data=element), divider(data=element)) for element in data]
     if not content:
-        content = specialMatches(context.tag, 'pattern', 'empty')
+        content = context.allPatterns('empty')
     else:
         ## No divider after the last thing.
         content[-1] = content[-1][0]
-    footers = specialMatches(context.tag, 'pattern', 'footer')
+    footers = context.allPatterns('footer')
     
-    # clone is necessary here because headers and footers are generators that
-    # haven't run yet, which depend on context.tag's contents.
-    return context.tag.clone(deep=False).clear()[ headers, content, footers ]
+    return context.tag.clear()[ headers, content, footers ]
 
 
 def mapping(context, data):
     for k, v in data.items():
-        for slot in context._locateSlots(k):
-            slot[v]
+        context.fillSlots(k, v)
     return context.tag
 
 
Index: tags.py
===================================================================
RCS file: /cvs/Quotient/nevow/tags.py,v
retrieving revision 1.9
diff -u -r1.9 tags.py
--- tags.py	30 Jan 2004 18:52:47 -0000	1.9
+++ tags.py	10 Feb 2004 21:26:17 -0000
@@ -4,7 +4,7 @@
 # Public License as published by the Free Software Foundation.
 
 
-from nevow.stan import Proto, Tag, directive, xml, CommentProto, invisible
+from nevow.stan import Proto, Tag, directive, xml, CommentProto, invisible, slot
 
 
 comment = CommentProto()
Index: serial/flatmdom.py
===================================================================
RCS file: /cvs/Quotient/nevow/serial/flatmdom.py,v
retrieving revision 1.14
diff -u -r1.14 flatmdom.py
--- serial/flatmdom.py	5 Feb 2004 16:57:50 -0000	1.14
+++ serial/flatmdom.py	10 Feb 2004 21:26:17 -0000
@@ -6,36 +6,42 @@
 from __future__ import generators
 
 from nevow.renderer import serialize
-from nevow.stan import Tag, xml, directive
+from nevow.stan import Tag, xml, directive, slot, invisible
 
 from twisted.python import components
 
 def MicroDomTextSerializer(original, context):
     if original.raw:
-        yield original.nodeValue
+        return original.nodeValue
     else:
         from twisted.xish.domish import escapeToXml
-        yield escapeToXml(original.nodeValue)
+        return escapeToXml(original.nodeValue)
 
 
 def MicroDomCommentSerializer(original, context):
-    yield xml("<!--%s-->" % original.data)
+    return xml("<!--%s-->" % original.data)
     
 def MicroDomEntityReferenceSerializer(original, context):
-    yield xml(original.nodeValue)
+    return xml(original.nodeValue)
 
 
-def MicroDomElementSerializer(original, context):
+def MicroDomElementSerializer(element, context):
     directiveMapping = {
         'render': 'renderer',
         'data': 'data',
-        'observer': 'observer'
     }
     attributeList = [
-        'pattern', 'slot', 'macro', 'fill-slot', 'key',
+        'pattern', 'key',
     ]
 
-    element = original
+    name = element.tagName
+    if name.startswith('nevow:'):
+        _, name = name.split(':')
+        if name == 'invisible':
+            name = ''
+        elif name == 'slot':
+            return slot(element.attributes['name'])
+        
     attrs = dict(element.attributes) # get rid of CaseInsensitiveDict
     specials = {}
     attributes = attributeList
@@ -54,14 +60,14 @@
             specials[nons] = v
             del attrs[k]
 
-    yield serialize(
-        Tag(
-            element.tagName,
+    tag = Tag(
+            name,
             attributes=attrs,
             children=element.childNodes,
             specials=specials
-        ),
-        context)
+            )
+
+    return serialize(tag, context)
 
 
 def MicroDomDocumentSerializer(original, context):
Index: serial/flatsax.py
===================================================================
RCS file: /cvs/Quotient/nevow/serial/flatsax.py,v
retrieving revision 1.4
diff -u -r1.4 flatsax.py
--- serial/flatsax.py	6 Feb 2004 22:54:31 -0000	1.4
+++ serial/flatsax.py	10 Feb 2004 21:26:17 -0000
@@ -19,12 +19,11 @@
     directiveMapping = {
         'render': 'renderer',
         'data': 'data',
-        'observer': 'observer'
     }
     attributeList = [
-        'pattern', 'slot', 'macro', 'fill-slot', 'key',
+        'pattern', 'key',
     ]
-    
+
     def resolveEntity(self, publicId, systemId):
         ## This doesn't seem to get called, which is good.
         raise Exception("resolveEntity should not be called. We don't use external DTDs.")
@@ -45,6 +44,17 @@
         self.current.append(xml("<?%s %s?>\n" % (target, data)))
 
     def startElement(self, name, attrs):
+        if name.startswith('nevow:'):
+            _, name = name.split(':')
+            if name == 'invisible':
+                name = ''
+            elif name == 'slot':
+                el = slot(element.attributes['name'])
+                self.current.append(el)
+                fakeEl = Tag('') # eat children
+                self.current = fakeEl
+                self.stack.append(self.current)
+        
         attrs = dict(attrs)
         specials = {}
         attributes = self.attributeList
Index: serial/flatstan.py
===================================================================
RCS file: /cvs/Quotient/nevow/serial/flatstan.py,v
retrieving revision 1.25
diff -u -r1.25 flatstan.py
--- serial/flatstan.py	6 Feb 2004 23:03:47 -0000	1.25
+++ serial/flatstan.py	10 Feb 2004 21:26:17 -0000
@@ -19,7 +19,7 @@
                   'input', 'col', 'basefont', 'isindex', 'frame')
 
 def ProtoSerializer(original, context):
-    yield xml('<%s />' % original)
+    return '<%s />' % original
 
 
 def TagSerializer(original, context, contextIsMine=False):
@@ -65,26 +65,26 @@
             yield serialize(child, context)
         return
     
-    yield xml('<%s' % original.tagName)
+    yield '<%s' % original.tagName
     if original.attributes:
         attribContext = WovenContext(parent=context, precompile=context.precompile, isAttrib=True)
         for (k, v) in original.attributes.iteritems():
             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)
+            yield ' %s="' % k
             yield serialize(v, attribContext)
-            yield xml('"')
+            yield '"'
     if not original.children:
         if original.tagName in allowSingleton:
-            yield xml(' />')
+            yield ' />'
         else:
-            yield xml('></%s>' % original.tagName)
+            yield '></%s>' % original.tagName
     else:
-        yield xml('>')
+        yield '>'
         for child in original.children:
             yield serialize(child, context)        
-        yield xml('</%s>' % original.tagName)
+        yield '</%s>' % original.tagName
 
 def StringSerializer(original, context):
     ## quote it
@@ -98,7 +98,7 @@
 
 
 def NoneWarningSerializer(original, context):
-    return xml('<span style="position: relative; font-size: 100; font-weight: bold; color: red; border: thick solid red;">None</span>')
+    return '<span style="position: relative; font-size: 100; font-weight: bold; color: red; border: thick solid red;">None</span>'
 
 
 def StringCastSerializer(original, context):
@@ -175,13 +175,19 @@
     renderer = rendererFactory.renderer(context, original.name)
     return serialize(renderer, context)
 
+def SlotSerializer(original, context):
+    if context.precompile:
+        return original
+    data = context._locateSlotData(original.name)
+    return serialize(data, context)
+
 
 def ContextSerializer(original, context):
     originalContext = original.clone(deep=False)
     originalContext.precompile = context and context.precompile or False
     originalContext.chain(context)
     try:
-        return flatten(TagSerializer(originalContext.tag, originalContext, contextIsMine=True))
+        return TagSerializer(originalContext.tag, originalContext, contextIsMine=True)
     except:
         from twisted.web import util
         from twisted.python import failure
@@ -200,7 +206,7 @@
 
 
 def CommentSerializer(original, context):
-    yield xml("<!--")
+    yield "<!--"
     for x in original.children:
         yield serialize(x, context)
-    yield("-->")
+    yield "-->"
Index: test/test_disktemplate.py
===================================================================
RCS file: /cvs/Quotient/nevow/test/test_disktemplate.py,v
retrieving revision 1.18
diff -u -r1.18 test_disktemplate.py
--- test/test_disktemplate.py	17 Jan 2004 22:49:16 -0000	1.18
+++ test/test_disktemplate.py	10 Feb 2004 21:26:17 -0000
@@ -144,8 +144,8 @@
         temp = self.mktemp()
         open(temp,'w').write("""
         <table nevow:data="aDict" nevow:render="slots">
-        <tr><td nevow:slot="1"></td><td nevow:slot="2"></td></tr>
-        <tr><td nevow:slot="3"></td><td nevow:slot="4"></td></tr>
+        <tr><td><nevow:slot name="1" /></td><td><nevow:slot name="2" /></td></tr>
+        <tr><td><nevow:slot name="3" /></td><td><nevow:slot name="4" /></td></tr>
         </table>""")
     
         class Renderer(renderer.HTMLRenderer):
@@ -153,8 +153,7 @@
                 return {'1':'one','2':'two','3':'three','4':'four'}
             def render_slots(self,context,data):
                 for name,value in data.items():
-                    for slot in context._locateSlots(name):
-                        slot[value]
+                    context.fillSlots(name, value)
                 return context.tag
 
         result = deferredRender(Renderer(templateFile=temp))
@@ -163,17 +162,17 @@
             "<table><tr><td>one</td><td>two</td></tr><tr><td>three</td><td>four</td></tr></table>",
             "Whoops. We didn't get what we expected!")
 
-    def test_keys(self):
+    def test_patterns(self):
         temp = self.mktemp()
         open(temp,'w').write("""<span nevow:render="foo">
-			<span nevow:key="one">ONE</span>
-			<span nevow:key="two">TWO</span>
-			<span nevow:key="three">THREE</span>
+			<span nevow:pattern="one">ONE</span>
+			<span nevow:pattern="two">TWO</span>
+			<span nevow:pattern="three">THREE</span>
 		</span>""")
 
         class Mine(renderer.HTMLRenderer):
             def render_foo(self, context, data):
-                return context.keyed(data)
+                return context.allPatterns(data)
 
         result = deferredRender(Mine("one", templateFile=temp))
         self.assertEquals(result, '<span>ONE</span>')
Index: test/test_flatstan.py
===================================================================
RCS file: /cvs/Quotient/nevow/test/test_flatstan.py,v
retrieving revision 1.21
diff -u -r1.21 test_flatstan.py
--- test/test_flatstan.py	6 Feb 2004 21:21:52 -0000	1.21
+++ test/test_flatstan.py	10 Feb 2004 21:26:17 -0000
@@ -201,8 +201,8 @@
                 tags.table(data={'one': 1, 'two': 2}, renderer=stan.mapping)[
                     tags.tr[tags.td["Header one."], tags.td["Header two."]],
                     tags.tr[
-                        tags.td["One: ", tags.invisible(slot="one")],
-                        tags.td["Two: ", tags.invisible(slot="two")]
+                        tags.td["One: ", tags.slot("one")],
+                        tags.td["Two: ", tags.slot("two")]
                     ]
                 ]
             ]
Index: test/test_stan.py
===================================================================
RCS file: /cvs/Quotient/nevow/test/test_stan.py,v
retrieving revision 1.3
diff -u -r1.3 test_stan.py
--- test/test_stan.py	28 Oct 2003 23:41:15 -0000	1.3
+++ test/test_stan.py	10 Feb 2004 21:26:17 -0000
@@ -40,18 +40,19 @@
         self.assertEquals(clone.children, ["How are you"])
         self.assertNotIdentical(clone.children, tag.children)
 
+    ## TODO: need better clone test here to test clone(deep=True),
+    ## and behavior of cloning nested lists.
+
     def test_clear(self):
         tag = proto["these are", "children", "cool"]
         tag.clear()
         self.assertEquals(tag.children, [])
 
     def test_specials(self):
-        tag = proto(data=1, renderer=str, observer="1", pattern="item", slot="aSlot", macro="aMacro", **{'fill-slot': "someSlot"})
+        tag = proto(data=1, renderer=str, remember="stuff", key="myKey", **{'pattern': "item"})
         self.assertEquals(tag.data, 1)
-        self.assertEquals(tag.renderer, str)
-        self.assertEquals(tag.observer, "1")
+        self.assertEquals(getattr(tag, 'renderer'), str)
+        self.assertEquals(tag.remember, "stuff")
+        self.assertEquals(tag.key, "myKey")
         self.assertEquals(tag.pattern, "item")
-        self.assertEquals(tag.slot, "aSlot")
-        self.assertEquals(tag.macro, "aMacro")
-        self.assertEquals(getattr(tag, 'fill-slot'), "someSlot")
 

--Apple-Mail-2--642798763--