durusmail: quixote-users: Re: A toy Nevow implementation
A toy Nevow implementation
2004-01-15
2004-01-15
2004-01-15
2004-01-15
Debug syntax errors in PTL (was: A toy Nevow implementation)
2004-01-15
Debug syntax errors in PTL (was: A toy Nevowimplementation)
2004-01-15
Debug syntax errors in PTL (was: A toy Nevowimplementation)
2004-01-16
Jason E. Sibre (2 parts)
Debug syntax errors in PTL
2004-01-22
Debug syntax errors in PTL
2004-01-18
2004-01-18
2004-01-15
Re: A toy Nevow implementation
2004-01-16
2004-01-19
Re: A toy Nevow implementation
2004-01-19
Re: A toy Nevow implementation
2004-01-19
2004-01-19
2004-01-19
2004-01-19
2004-01-19
2004-01-19
2004-01-19
2004-01-19
2004-01-20
2004-01-20
2004-01-20
2004-01-20
2004-01-21
2004-01-20
Re: A toy Nevow implementation
Graham Fawcett
2004-01-19
I've written a slightly cleaner Nevow implementation. New features:

-- each tag has a 'children' attribute, which is a list of child
elements. (As before, tag attributes are contained in a dictionary
attribute called 'attribs'.) This makes for cleaner tree-traversal code.

     # example: Remove empty items from a list
     for item in mylist.children[:]:
         if len(item.children) == 0:
             mylist.children.remove(item)

Oscar, since you're interested in tree traversal, could you play with
the code, and perhaps improve upon its 'traversal interface'?


-- I added a primitive validation mechanism. Example code:

 >>> from nevow import Tag
 >>> ul = Tag('ul', contains=['li']) # may only contain LI elements
 >>> ul.contains = ['li']            # another way to do it.
 >>> li = Tag('li')                  # no 'contains' --> no validation
 >>> p = Tag('p')
 >>> mylist = ul[li['hello']]        # OK: LI inside UL
 >>> myitem = li[p['bonjour']]       # OK: no validation on LI children
 >>> badlist = ul[p['invalid']]      # P in UL not allowed!
Traceback: ....
nevow.ValidationError: 
    may not contain

    Note that the HTML tags that I've created in the nevow module do *not* have 'contains' rules; therefore they do no validation. By default, tags will auto-validate their contents when they are initialized, and when they are copied via a __getitem__ call. This behaviour can be turned off by setting the attribute auto_validate=0 on the prototype, or by clearing the contains list of the prototype. See the code for more detail. You can also validate explicitly by calling tag.validate(recurse=bool), where bool defaults to 0 (false). The current validate() method asserts that 'child.name in self.contains' is true for all children, and calls child.validate() if recursion is specified. However, if self.contains is an empty list (the default), then no validation is done, even on children. Note that not all children are Tag objects: some are TextNodes and others may be CallableNodes (lazy-evaluated functions). Neither of these are validated; it's always assumed that text (and callable output) are valid. We might want an expand() method (like a macro expansion) to expand callables in order to validate their output before rendering. I don't claim that this is a great validation scheme, I just hope to get a taste of what one might be like. Andrew, was this kind of what you had in mind w.r.t. validation? -- Graham

"""
nevow.py: Basic Nevow-style tags for Quixote (or any other purpose).

Nevow is the next generation templating system for Twisted.Web.
See http://stewstuff.com/doc/nevow.xhtml for details.

This is a clean-room, toy implementation. Not sure about the
real Nevow, but this code uses a prototype-based approach.
All tag "classes" are really objects, and return modified copies
of themselves under certain conditions.

Graham Fawcett
2004.01.14 -- first revision.
2004.01.19 -- added 'children' attribute, for tree manips.
"""

from quixote.html import htmltext


class ValidationError(Exception):
    pass


class Tag:

    def __init__(self, name, children=None, attribs=None,
                 contains=None, auto_validate=1):
        """
        Nevow-like tag objects.
        name: tag name (e.g. 'html', 'div', ...)
        children: list of child elements
        """
        self.name = name
        self.children = children and children or []
        self.attribs = attribs and attribs or {}
        self.contains = contains and contains or []
        for element in self.contains:
            assert isinstance(element, str), \
                'contains attribute must contain string values!'
        self.auto_validate = auto_validate
        if auto_validate:
            self.validate(recurse=0)

    def __call__(self, *args, **kwargs):
        if len(args):
            kwargs.update(args[0])
        d = self.attribs.copy()
        d.update(kwargs)
        dup = Tag(self.name, None, attribs=d)
        return dup

    def __getitem__(self, content):
        if not isinstance(content, tuple):
            content = (content,)
        children = [wrap(child) for child in content]
        return Tag(self.name, children, attribs=self.attribs,
                   contains=self.contains, auto_validate=self.auto_validate)

    def validate(self, recurse=1):
        if self.contains:
            for child in self.children:
                if isinstance(child, Tag):
                    if not child.name in self.contains:
                        raise ValidationError, \
                            '<%s> may not contain <%s>' % (self.name,
child.name)
                    if recurse:
                        child.validate(recurse)

    def render(self):
        x = []
        x.append('<%s' % self.name)
        if self.attribs:
            for k, v in self.attribs.items():
                x.append(' %s="%s"' % (k, v))
        if self.children:
            x.append('>')
            for child in self.children:
                x.append(child.render())
            x.append('' % self.name)
        else:
            x.append(' />')
        return ''.join(x)

    def __repr__(self):
        return '<%s:%s>' %  (self.__class__.__name__, self.name)

    def __str__(self):
        return self.render()

    def htmltext(self):
        return htmltext(str(self))


class TextNode:
    def __init__(self, data):
        self.data = str(data)
    def render(self):
        return self.data


class CallableNode:
    def __init__(self, func):
        self.func = func
    def render(self):
        return wrap(self.func()).render()


def wrap(element):
    if isinstance(element, Tag):
        #print 'wrap returns %s' % children.name
        return element
    elif callable(element):
        return CallableNode(element)
    elif type(element) in (tuple, list):
        print 'warning'
        return [wrap(sub) for sub in element]
    else:
        return TextNode(element)

#----------------------------------------------------------
# some basic HTML 4 tag names. extend as you please.

tagnames = (
    'html head meta script title style link body p h1 h2 h3 h4 h5 h6 '
    'div table tr td tbody ol ul li a hr img object embed '
)

for tagname in tagnames.split(' '):
    if tagname:
        cmd = '%s = Tag("%s")' % (tagname, tagname)
        exec cmd

reply