Nevow: A Web Application Construction Kit

Donovan Preston <dp@divmod.org>

Timeslot

60 minute presentation

Author background

Donovan Preston developed nevow for Divmod, where he uses it to develop the web interface for Quotient, a personal communications management server. He previously worked for InterSight, where he developed Woven for PublishWorks, an automated publishing system using Python, and two smaller applications in both Zope and WebWare.

Summary

Nevow is a next-generation web application templating system, based on the ideas developed in the Twisted Woven package. Its main focus is on separating the HTML template from both the business logic and the display logic, while allowing the programmer to write pure Python code as much as possible. It separates your code into 'data' and 'render' functions, a simplified implementation of traditional MVC. It has various parts which can be used individually or as a whole, integrated web solution:

  • XHTML templates: contain no programming logic, only nodes tagged with nevow attributes
  • data/render methods: simplified MVC
  • stan: An s-expression-like syntax for expressing xml in pure python
  • formless: For describing the types of objects which may be passed to methods of your classes, validating and coercing string input from either web or command-line sources, and calling your methods automatically once validation passes
  • freeform: For rendering web forms based on formless type descriptions, accepting form posts and passing them to formless validators, and rendering error forms in the event validation fails
  • livepage: Cross-browser JavaScript glue for sending client side events to the server and server side events to the client after the page has loaded, without causing the entire page to refresh

Disk based templates

Nevow includes the ability to load templates off disk. These templates may have processing directives which cause the execution of python methods at render time. The attribute technique was inspired by the attributes used by ZPT. However, no actual code may be embedded in the HTML template:

<html>
  <head>
    <title>Greetings!</title>
  </head>
  <body>
    <h1 style="font-size: large">Now I will greet you:</h1>
    <span nevow:render="greet" />
  </body>
</html>

This template can then be loaded and rendered like so:

class Greeter(renderer.HTMLRenderer):
    templateFile = "Greeting.html"

    def render_greet(self, context, data):
        return random.choice(["Hello", "Greetings", "Hi"]), " ", data

Greeter("My name is").renderString()

data/render methods

To allow clean isolation between code which fetches data from a data source and code which renders the data into HTML, nevow allows you to write both 'data' methods and 'render' methods. These concepts are inspired by MVC, but simpler, since the framework can handle most of the controller aspect. An example:

<html>
  <body>
    <span nevow:data="name" render="colorful" />
    <span nevow:data="fun" render="colorful" />
  </body>
</html>

This template can be loaded and rendered using a class such as this:

class Colorful(renderer.HTMLRenderer):
  templateFile = "Colorful.html"
  
  def render_colorful(self, context, data):
    color = random.choice(['red', 'green', 'blue'])
    return context.tag(style="color: %s" % color)

  def data_name(self, context, data):
    return "Your name here"

  def data_fun(self, context, data):
    return "Are we having fun yet?"

Stan

One of the most powerful things about nevow is stan, an s-expression-like syntax for producing XML fragments in pure Python syntax. Stan is not required for using nevow, but it is both a simple and powerful way to both lay out one's XHTML templates and express one's display logic. A brief example will illustrate its utility:

import random
from nevow import renderer, tags


class Greeter(renderer.Renderer):
    def greet(self, context, data):
        return random.choice(["Hello", "Greetings", "Hi"]), " ", data

    document = tags.html[
    tags.head[ tags.title[ "Greetings!" ]],
    tags.body[
        tags.h1(style="font-size: large")[ "Now I will greet you:" ],
        greet
    ]
]

When the Greeter class is constructed, it is passed a Python object which will be used as that page's data:

Greeter("Your name here").renderString()

Formless

Python is dynamically typed, which means it has no built-in controls for enforcing the types of objects which are passed to one's methods. This is great for programmers, but not necessarily great if you are going to be passing user-entered input to those methods. Formless is a simple way to describe the types of objects that can be passed to one's methods, as well as coerce from string input to those types. Other code can then accept user input from a command line or from a web form, validate the input against the types described using formless, and call the method once validation has passed. A simple example:

from nevow.formless import TypedInterface, Integer, String

class ISimpleMethod(TypedInterface):
    def simple(self, 
        name=String(description="Your name."), 
        age=Integer(description="Your age.")):
        """Simple
        
        Please enter your name and age.
        """

class Implementation(object):
    __implements__ = ISimpleMethod,

    def simple(self, name, age):
        print "Hello, %s, who is %s" % (name, age)

Freeform

Freeform is a nevow module which will automatically render web forms and accept form posts based on types described using the classes in formless. Used in conjunction with the twisted.web HTTP server, the process is almost automatic:

from nevow import renderer, tags
from nevow import freeform

class WebForm(renderer.Renderer):
    document = tags.html[
    tags.body[
        h1["Here is the form:"],
        freeform.configure
    ]
]

resource = WebForm(Implementation())

Exposing this resource instance to the web using twisted.web and visiting it will cause a form with two input boxes to be rendered. Posting the form will cause form validation to occur. Upon error, the user will be returned to the original page, with the form annotated with error messages. Upon success, the "simple" method of the Implementation instance will be called and passed a string and an integer.

LivePage

LivePage was a Woven technology which allowed programmers to receive server-side notification of client-side JavaScript events, and to send JavaScript to the client in response to a server-side event. It has not yet been fully implemented for nevow, but an implementation is planned in the near future. When implemented, the usage would look something like this:

from nevow import livepage

def greeter(user, nodeName):
      user.sendScript("alert('Greetings. You clicked the %s node.')" % nodeName)

# Any string arguments after the event handler function will be evaluated
# as JavaScript in the context of the web browser and results passed to the
# Python event handler
handler = livepage.eventHandler(greeter, 'this.name')

class Live(renderer.Renderer):
    document = tags.html[
    tags.body[
        ol[
            li(onclick=handler, name="one")["One"]
            li(onclick=handler, name="two")["Two"]
            li(onclick=handler, name="three")["Three"]
        ]
    ]
]

Conclusion

The nevow package contains many tools which are useful for performing web and XML related tasks, from generating simple XML documents using easy-to-write pure-python syntax to building a full-blown, highly interactive web application. Nevow was designed to allow application programmers to remove all logic constructs from HTML templates and to give them the power of pure Python as often as possible. It attempts to provide tools for writing one's application which allow you to do so as expressively as possible, so you can focus on the important parts of one's application instead of the mechanics of the web, while still allowing access to the low level details of HTML and HTTP when necessary.

Presentation Outline

  1. Introduction - Who am I?
  2. What is nevow?
  3. How is nevow related to Woven and Twisted?
  4. HTML Templates
  5. data/render: Keeping presentation and data separate
  6. Integrating HTML templates from designers into your application
  7. stan -- Expressing XML in Python
  8. Rendering pages over HTTP using twisted.web
  9. Using nevow from CGIs and other web environments
  10. What are specials?
  11. Special: pattern
  12. Special: slot
  13. Special: key
  14. Built-in renderers
  15. Finding specials from your render functions
  16. Using the context
  17. Using the request
  18. Using the session
  19. Using guard to restrict access to your web resources
  20. Writing a CredentialsChecker for guard
  21. Formless: Describing your method interfaces
  22. Formless: Describing property types
  23. Formless: Grouping bindings together
  24. Formless: Writing a Typed subclass to implement validation and coersion
  25. Using freeform to render forms
  26. Freeform -- You get form handling for free
  27. Freeform error reporting
  28. Customizing your forms
  29. Conclusion
  30. Q&A