debian/0000755000000000000000000000000012235042602007162 5ustar debian/python2.3-nevow.manpages0000644000000000000000000000003310205470040013567 0ustar doc/man/nevow-xmlgettext.1 debian/source/0000755000000000000000000000000012235042602010462 5ustar debian/source/format0000644000000000000000000000001411343264601011674 0ustar 3.0 (quilt) debian/patches/0000755000000000000000000000000012235042602010611 5ustar debian/patches/2700-unittests-fix.patch0000644000000000000000000000401012235041067015045 0ustar Author: Jean-Paul Calderone Forwarded: not-needed Description: Update two Nevow unit tests to work with more recent versions of Twisted This will introduce a test failure against Twisted < 11.0, but no implementation changes are actually being made, so no actual functionality should be lost on old Twisted versions. --- Nevow/nevow/test/test_appserver.py 2007-12-20 15:51:48 +0000 +++ Nevow/nevow/test/test_appserver.py 2013-01-01 21:44:51 +0000 @@ -170,14 +170,20 @@ assert proto.transport.disconnecting return proto + + def setSiteTime(self, when): + """ + Forcibly override the current time as known by C{self.site}. + + This relies on knowledge of private details of + L{twisted.web.server.Site}. It would be nice if there were an API on + that class for doing this more properly, to facilitate testing. + """ + self.site._logDateTime = when + + def test_oldStyle(self): - # need to try the new location first to make _logDateTime - # faking work - try: - from twisted.web import http - except ImportError: - from twisted.protocols import http - http._logDateTime = 'faketime' # ugly :( + self.setSiteTime('faketime') proto = self.renderResource('/foo') logLines = proto.site.logFile.getvalue().splitlines() self.assertEquals(len(logLines), 1) === modified file 'Nevow/nevow/test/test_errorhandler.py' --- Nevow/nevow/test/test_errorhandler.py 2006-04-14 17:23:46 +0000 +++ Nevow/nevow/test/test_errorhandler.py 2012-12-31 00:27:39 +0000 @@ -79,7 +79,7 @@ def later((code, html)): self.assertEquals(rend.FourOhFour.notFound, html) self.assertEquals(code, 404) - fe = log.flushErrors(BrokenException) + fe = self.flushLoggedErrors(BrokenException) self.assertEquals(len(fe), 1) return renderResource('/foo', notFoundHandler=BadNotFoundHandler()).addCallback(later) debian/patches/02-fix-web-log-test.patch0000644000000000000000000000162612235037454015164 0ustar Author: Vincent Bernat Forwarded: not-needed Bug-Debian: http://bugs.debian.org/625151 Description: _logDateTime is a per-instance variable set to None on __init__. --- n/nevow/test/test_appserver.py~ 2007-12-20 16:51:48.000000000 +0100 +++ n/nevow/test/test_appserver.py 2011-05-18 13:49:38.866464923 +0200 @@ -154,6 +154,7 @@ return "boring" self.site = appserver.NevowSite(Res1()) self.site.logFile = StringIO() + self.site._logDateTime = 'faketime' # ugly :( def tearDown(self): del self.site @@ -177,7 +178,6 @@ from twisted.web import http except ImportError: from twisted.protocols import http - http._logDateTime = 'faketime' # ugly :( proto = self.renderResource('/foo') logLines = proto.site.logFile.getvalue().splitlines() self.assertEquals(len(logLines), 1) debian/patches/series0000644000000000000000000000016412235042027012030 0ustar 01-add-doc-from-svn.patch 2662-deprecation-warnings.patch 2682-zope-interface-compat.patch 2700-unittests-fix.patch debian/patches/2682-zope-interface-compat.patch0000644000000000000000000000710012235040761016427 0ustar Author: Tristan Seligmann Forwarded: not-needed Description: Remove the indirect-attribute trickery in TypedInterface (which no longer works with newer versions of z.i) in favour of setting attributes directly on the interface (which seems to have started working quite some time ago). --- Nevow/formless/annotate.py 2010-02-06 04:33:05 +0000 +++ Nevow/formless/annotate.py 2011-08-06 17:57:26 +0000 @@ -677,9 +677,6 @@ return None, '\n'.join(docs) -_typedInterfaceMetadata = {} - - class MetaTypedInterface(InterfaceClass): """The metaclass for TypedInterface. When TypedInterface is subclassed, this metaclass' __new__ method is invoked. The Typed Binding introspection @@ -716,21 +713,14 @@ Once the metaclass __new__ is done, the Foo class instance will have three properties, __methods__, __properties__, and __spec__, """ - __methods__ = property(lambda self: _typedInterfaceMetadata[self, '__methods__']) - __id__ = property(lambda self: _typedInterfaceMetadata[self, '__id__']) - __properties__ = property(lambda self: _typedInterfaceMetadata[self, '__properties__']) - __spec__ = property(lambda self: _typedInterfaceMetadata[self, '__spec__']) - name = property(lambda self: _typedInterfaceMetadata[self, 'name']) - label = property(lambda self: _typedInterfaceMetadata[self, 'label']) - description = property(lambda self: _typedInterfaceMetadata[self, 'description']) - default = property(lambda self: _typedInterfaceMetadata.get((self, 'default'), 'DEFAULT')) - complexType = property(lambda self: _typedInterfaceMetadata.get((self, 'complexType'), True)) def __new__(cls, name, bases, dct): rv = cls = InterfaceClass.__new__(cls) - _typedInterfaceMetadata[cls, '__id__'] = nextId() - _typedInterfaceMetadata[cls, '__methods__'] = methods = [] - _typedInterfaceMetadata[cls, '__properties__'] = properties = [] + cls.__id__ = nextId() + cls.__methods__ = methods = [] + cls.__properties__ = properties = [] + cls.default = 'DEFAULT' + cls.complexType = True possibleActions = [] actionAttachers = [] for key, value in dct.items(): @@ -825,21 +815,21 @@ attacher.attachActionBindings(possibleActions) methods.sort(_sorter) properties.sort(_sorter) - _typedInterfaceMetadata[cls, '__spec__'] = spec = methods + properties + cls.__spec__ = spec = methods + properties spec.sort(_sorter) - _typedInterfaceMetadata[cls, 'name'] = name + cls.name = name # because attributes "label" and "description" would become Properties, # check for ones with an underscore prefix. - _typedInterfaceMetadata[cls, 'label'] = dct.get('_label', None) - _typedInterfaceMetadata[cls, 'description'] = dct.get('_description', None) + cls.label = dct.get('_label', None) + cls.description = dct.get('_description', None) defaultLabel, defaultDescription = labelAndDescriptionFromDocstring(dct.get('__doc__')) if defaultLabel is None: defaultLabel = nameToLabel(name) - if _typedInterfaceMetadata[cls, 'label'] is None: - _typedInterfaceMetadata[cls, 'label'] = defaultLabel - if _typedInterfaceMetadata[cls, 'description'] is None: - _typedInterfaceMetadata[cls, 'description'] = defaultDescription + if cls.label is None: + cls.label = defaultLabel + if cls.description is None: + cls.description = defaultDescription return rv debian/patches/01-add-doc-from-svn.patch0000644000000000000000000036646512235037331015137 0ustar Author: Vincent Bernat Forwarded: not-needed Description: Add documentation from SVN To get it: $ svn co http://divmod.org/svn/Divmod/trunk/Nevow/ Nevow-svn $ cp -a Nevow-svn Nevow-svn-withoutdoc $ rm Nevow-svn-withoutdoc/**/*.{xhtml,tpl} $ diff -Naur Nevow-svn-withoutdoc Nevow-svn diff -Naur Nevow-0.10.0/doc/howto/chattutorial/concepts.xhtml Nevow-0.10.0-new/doc/howto/chattutorial/concepts.xhtml --- Nevow-0.10.0/doc/howto/chattutorial/concepts.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/chattutorial/concepts.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,136 @@ + + + + + Concepts + + + +

Concepts

+ +

Servers and Clients

+ +

COMET applications can seem an almost impenetrable mess when one is first +learning about them, much like when writing event-based desktop +applications. However, there are some basic concepts that we can emphasize now +to circumvent or alleviate most of the confusion.

+ +

In principle, the problem is very simple:

+
    +
  • We want out users to interact with a web page with out having to refresh +the page, and we want new data and/or views to be rendered in response to our +users' actions;
  • +
  • We want the ability to push updates to user pages from the server to the +browser, when the server has new data or views that are ready.
  • +
+ +

As usual, the implementation of the solution is much more complicated than +the statement of the problem, but hopefully the way that we have designed +Athena will hide those implementation details while providing powerful tools to +build the applications you envision. So, let's take a look at what you need to +know about servers and clients when building Athena web applications.

+ +

It is crucial that one understands that when we write Athena applications, +we are doing a few different things:

+
    +
  • Writing server code in Python that performs server actions
  • +
  • Writing server code in Python that makes remote calls to the browser
  • +
  • Writing browser code in JavaScript that performs browser actions
  • +
  • Writing browser code in JavaScript that makes remote calls to the server
  • +
+ +

Since server-on-server and client-on-client are rather common place and +generally well understood, we will ignore those for now. As the other two +are the focus of AJAX/COMET and thus also the primary domain of Athena, that is +what we will discuss below.

+ +

Browser-to-server calls are made by Athena via the now-famous + XMLHttpRequest. Server-to-browser calls are opened by the browser ahead of +time, and when the server is ready, the data is sent to the browser via that +connection.

+ +

JavaScript: Making Calls to the Server

+ +

When creating the JavaScript portion of our applications, we subclass +an Athena JavaScript widget, which has a method named +callRemote(). By utilizing this method, we can send messages from +our JavaScript client to the server (as long as the method we call exists in +the server code).

+ +

For example, in the chat application we will be building in this series +of tutorials, we will have a JavaScript class called ChatterBox with a +say() method, like the following:

+
+function say(self, msg) {
+    self.callRemote("say", msg);
+    // Now show the text to the user somehow...
+}
+
+

This will make a remote call to the Python server code, executing the +say() method and passing the msg variable as a +parameter.

+ +

In Athena, the relationship between the browser code and the server code is +established by declaring the JavaScript module in the Python server code, in +the following manner:

+
+class ChatterBox(LiveElement):
+    jsClass = u'ChatThing.ChatterBox'
+
+

Additionally, in order for the JS to be able to make a call to remote Python +code, the Python method has to be exposed. This is a security feature, +implemented to ensure the JavaScript code can only call Python methods that +have been specifically marked as safe. Appropriately enough, this is done in +your Python class with the expose decorator:

+ +
+def say(self, text):
+    for chatter in chatRoom:
+        chatter.youHeardSomething(text)
+say = expose(say)
+
+ +

Python: Making Calls to the Browser

+ +

Now what about the COMET side of the equation? If we want our server to +update data in the browser, we need to be able to call JavaScript code from our +Python server. We use a similar Python method as the JavaScript one (when +making calls from the browser to the server), acquired when our Python class +inherited from nevow.athena.LiveElement:

+ +
+def hear(self, sayer, text):
+    self.callRemote("hear", sayer, text)
+
+ +

In order for this call to work, we need to have the hear() +method defined in our ChatterBox JavaScript class, and that will +look like this:

+
+function hear(self, avatarName, text) {
+    // Here, you'd show the user some text.
+}
+
+ +

Unlike on our Python classes, no special annotations need to be made on the +JavaScript side: all JavaScript methods on browser-side Widget objects are +allowed to be called by the server. If you've sent code to the browser, you've +already forfeited the ability to control when it's called. There wouldn't be a +point to limiting the server's rights to run its code when the user can freely +run it herself.

+ +

Summary

+ +

With the samples above, you should have a growing sense of how Python and +JavaScript interact as servers and clients in the world of Athena. In +particular, you should be getting a sense of how JavaScript and Python will be +interacting in your Athena applications.

+ +

This has just been a taste of Athena with a few peeks into the code we +will be writing. We will cover these topics in greater detail in the following +pages, within the context of creating a functional Athena application, +complete with step-by-step instructions and rationale.

+ + + diff -Naur Nevow-0.10.0/doc/howto/chattutorial/env.xhtml Nevow-0.10.0-new/doc/howto/chattutorial/env.xhtml --- Nevow-0.10.0/doc/howto/chattutorial/env.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/chattutorial/env.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,122 @@ + + + + + Setting up an Environment + + + +

Setting up an Environment

+ +

Install

+ +To run this tutorial, you need to have nevow available to python and +you'll need the files in the doc/howto tree. You don't even have to +install nevow; the examples will run within the source tree. + + +

Combinator: The Divmod Way

+ +

Using SVN +with Combinator +is the best way to try out the example code in-place (and hop between +other SVN branches in the future). This is how we develop and test our +applications at Divmod. If you have a system installation of Twisted +that you don't want to update or interfere with, you can use this +method without installing anything. +

+ +
    + +
  1. Create a projects directory or change to some other test directory +of your choice: +
    +$ mkdir ~/Projects
    +$ cd ~/Projects
    +
    +
  2. + + +
  3. If you don't have the +twisted library, check it out now: +
    +$ svn co svn://svn.twistedmatrix.com/svn/Twisted/trunk Twisted/trunk
    +
    +
  4. + +
  5. Then get Combinator and Nevow (and the rest of Divmod). See the +Combinator +Tutorial for more about these special checkout paths. +
    +$ svn co http://divmod.org/svn/Divmod/trunk Divmod/trunk
    +
    +
  6. + +
  7. Set up the Combinator environment in this shell. You'll need this +step in any future test shells since it adjusts PATH and PYTHONPATH: +
    +$ eval `python Divmod/trunk/Combinator/environment.py`
    +(some "link:" lines are normal)
    +
    +
  8. + +
  9. Register both the Twisted and Divmod (and thus Nevow+Athena) codebases with +Combinator: +
    +$ chbranch Twisted trunk
    +$ chbranch Divmod trunk
    +
    +
  10. + +
  11. You can check to see if your environment is ready to go by running the +tutorial tests (from any directory, after executing the previous command): +
    +$ trial nevow.test.test_howtolistings
    +
    +If they all pass, you're ready to begin the tutorial. +
  12. +
+ + + +

Standard distutils Installation

+ +

If you don't want to manage branches and environments with +Combinator, you can install our code in the +standard site-packages directory. You'll still need the +source tree so you can use the files in doc/howto.

+ +

For those that would prefer the old way, here's how you do it:

+ +
    + +
  1. Create a projects directory: +
    +$ mkdir ~/Projects
    +$ cd ~/Projects
    +
    +
  2. + +
  3. Checkout and install the latest Twisted: +
    +$ svn co svn://svn.twistedmatrix.com/svn/Twisted/trunk Twisted
    +$ cd Twisted
    +$ sudo python setup.py install
    +$ cd ../
    +
    +
  4. + +
  5. Checkout and install Nevow: +
    +$ svn co http://divmod.org/svn/Divmod/trunk/Nevow Nevow
    +$ cd Nevow
    +$ sudo python setup.py install
    +$ cd ../
    +
    +
  6. + +
+ + + diff -Naur Nevow-0.10.0/doc/howto/chattutorial/index.xhtml Nevow-0.10.0-new/doc/howto/chattutorial/index.xhtml --- Nevow-0.10.0/doc/howto/chattutorial/index.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/chattutorial/index.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,56 @@ + + + + + Nevow Athena from Scratch, or The Evolution of a Chat Application + + + +

Nevow Athena from Scratch, or The Evolution of a Chat Application

+ +

The Chat Tutorial Series

+ +

+Athena is the JavaScript engine behind Nevow, providing a great deal of +resources and power to the developer of asynchronous web applications. To +demonstrate this, we are using a web chat application as our primary example +in this tutorial. The tutorial is split into several parts: a few introductory +pages and then independent (but related) tutorials of increasing complexity. +

+ +
    +
  1. Basics + +
  2. +
  3. Toy Echo Application
  4. +
  5. Simple Chat and Two-Way Communications
  6. +
+ +

History

+

+Nevow's predecessor was Woven (and prior to that, WebMVC). Woven had something +called LivePage that was doing DOM manipulation as far back as +2002. In early 2003, Woven event handlers supported sending JavaScript back to +the user's browser, allowing pages to be updated in response to user-generated +events. The earliest publicly visible revisions of Nevow made use of XHR +(XMLHttpRequest) in early 2004. These facts are notable because Nevow was using +AJAX a year before the term was coined in 2005 and had working code in 2002 and +2003 that predated Netscape publishing articles on what they called Inner +Browsing where all navigation takes place withing a single page. +

+ +

+Again taking the lead, Athena offers features which developers cannot find +elsewhere. In this series, we attempt to expose these excellent qualities +to the world of application +developers. +

+ + + + diff -Naur Nevow-0.10.0/doc/howto/chattutorial/intro.xhtml Nevow-0.10.0-new/doc/howto/chattutorial/intro.xhtml --- Nevow-0.10.0/doc/howto/chattutorial/intro.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/chattutorial/intro.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,106 @@ + + + + + Introduction + + + +

Introduction

+ +

Who is this tutorial for?

+ +

This tutorial is for people who want to build interactive client-server +functionality where a web-browser is the client. It will show you how to build +a live, interactive chat application that requires nothing more than a web +browser that supports JavaScript.

+ +

The interesting thing about a chat application, which shows why Nevow Athena +is special, is that it involves two-way communication. In other words, it +involves not only the recently-popular AJAX (the web browser sending commands +to the server without loading a new page) but also the trickier and, in our +opinion, somewhat cooler technique known as COMET (the web server +sending commands to the browser).

+ +

Who is this tutorial not for?

+ +

Nevow Athena is not for people who want a normal web application +framework. If you want one of those, you should use +non-Athena-Nevow, +Django, +TurboGears, or maybe even +Ruby On Rails. Athena doesn't work in terms +of pages, links, or HTTP requests and responses; it is a client-server +framework that works in terms of widgets, JavaScript objects, and symmetric +asynchronous message queues.

+ +

However, as alluded to above, Athena is part of a larger framework, Nevow, +which can be used to build more general-purpose and traditional +web applications.

+ +

AJAX

+ +

AJAX isn't a technology in and of itself, bur rather an amalgam of +technologies used together in order to accomplish the goal of making web +applications more responsive than traditional delivery and interactive +mechanisms, such as HTML forms submitted to a server.

+ +

In particular, AJAX consists of the following:

+
    +
  • Asynchronous communications from a user's browser to a server
  • +
  • JavaScript
  • +
  • Exchanged data (usually XML or JSON)
  • +
+ +

COMET

+ +

Historically, the focus of AJAX technologies was user-event driven. However, +with the need to update the user's browser with events generated at the server, +a solution more sophisticated than AJAX was needed; this has been dubbed COMET. +Athena is implemented using both AJAX and COMET techniques, and therefore +allows two-way browser <-> server communications.

+ +

Athena Basics

+ +

We've provided brief background information on AJAX/COMET, but what is the +purpose of Athena? What makes Athena different than other solutions? Here are a +few key points that should help with these questions::

+
    +
  • Athena exists to make writing COMET web applications easy.
  • +
  • Athena is written in Python and JavaScript
  • +
  • It is written to be used with Nevow, a Twisted-based web framework.
  • +
  • Similar to Twisted's Perspective +Broker, Athena employs remote calls.
  • +
+ +

Athena was written by Twisted and Divmod developers (in addition to +contributing members of the community) in order to bring the outdated and +Nevow-incompatible Woven LivePage technology to Nevow. In addition, it was an +opportunity to improve upon the original design and incorporate new features to +address the growing needs of developers.

+ +

Target Applications

+ +

Good candidates for Athena web applications would include those where the +application needs to respond to user input and/or updates from servers, such +as the following:

+
    +
  • conference software (e.g. whiteboard, shared text, chat, etc.)
  • +
  • mail clients
  • +
  • interactive, multi-player games
  • +
  • social networking tools
  • +
  • office applications (e.g., spreadsheets, word processors, etc.)
  • +
+ +

Target Developers

+ +

Anyone who wants to create interactive, web-based applications is a +potential Nevow/Athena user. It's best to have some background in writing web +applications, and in addition, to know how to use Nevow. However, we hope that +this tutorial will be just as useful for beginners as experienced +developers.

+ + + diff -Naur Nevow-0.10.0/doc/howto/chattutorial/part00/index.xhtml Nevow-0.10.0-new/doc/howto/chattutorial/part00/index.xhtml --- Nevow-0.10.0/doc/howto/chattutorial/part00/index.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/chattutorial/part00/index.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,230 @@ + + + + + Nevow Athena from Scratch: Echo Application + + + +

What is an "Echo Application?"

+ +

+Our first foray into building an Athena application will be an easy venture: +we want to type something in an input box and have it echoed back to us on +the same page, without having to reload anything. Why? Well, our eventual +goal is to have a working chat server, with all sorts of technical bells +and whistles (persistent storage, authentication, +etc.), but that's a bit heady for right now. Many of the same principles +which we will eventually employ in our chat application exist for a simple +case of sending textual messages between a web browser and a server. This +is the essence of our "Echo" application. +

+ +

Mental Preparation

+ +

In the +Introduction and the +Concepts pages, we had a refresher on AJAX and +COMET and we learned a little bit about what that looks like for Athena. But +as we sit down to actually write an Athena application, what do we need to +wrap our heads around? +

+ +

Given the introductory knowledge we have, we know that we will need to +write some JavaScript, some Python, and if our past experience in developing +web applications is any guide, some form of template. This indeed is the +case, but here's something big: we're not working with pages and page +templates; we're working with "elements", or parts of the DOM tree. We will +not be creating page resources; we will be creating just the parts of a +"traditional" page that will be dynamic and interactive. +

+ +

Architecture

+ +

Now that we've pumped ourselves up and before we start clacking away at the +keyboard, we need to get pointed in the right direction. We need a +plan. Here's what we know:

+ +
    +
  1. We will have a server that: +
      +
    • serves dynamic elements in a resource accessible via a URL;
    • +
    • communicates with a client.
    • +
    +
  2. +
  3. We will have a client that: +
      +
    • communicates with the server;
    • +
    • updates its DOM tree.
    • +
    +
  4. +
+ +

The user experience of this application will be the following:

+
    +
  1. they will type text in an input on a form; and
  2. +
  3. the typed text will be rendered to a different part of the page upon +hitting a submit button.
  4. +
+ +

We will not simply write user input to a div with JavaScript +DOM manipulation, but will instead pass data like we expect will be necessary +when we write our chat application. After all, it's probably best to build +towards our goal. In order to accomplish this, the application will do +something like the following:

+ +
    +
  1. JavaScript client code will extract user input and send +it to our server;
  2. +
  3. Python code will receive messages from the client;
  4. +
  5. Python code will send messages to the client; and
  6. +
  7. a template file (or stan code) will be used for +presentation.
  8. +
+ +

+ +

Let the Coding Begin

+ +

In a future installment, we will outline the development process from +the perspective of test-driven development, in order to not only show how +to write unit tests for Athena (Python and JavaScript), but to encourage +good programming practices while working with Athena. For now, though, we will +just dive right in.

+ +

Presentation

+ +

Let's start with the easy bit: what our app will look like. Here is the +template for our echo application:

+ + + +

Things to note:

+
    +
  • This is not a complete HTML document, but is an XHTML template for an +"element".
  • +
  • The name space declarations in the top div tag are necessary +for the operation of Athena.
  • +
  • When we hit the "Send" button, our JavaScript class will call the +doSay() method.
  • +
+ +

Writing the Client

+ +

Next up is the JavaScript. We need to send our data to the server. In a +full chat application, it would be necessary to send the data to the server +so that we could propagate the message to all connected clients. In this +case, with the simple echo, we're not going to do anything with the data +that gets sent to the server, except send it back, of course.

+ +

Our JavaScript will need to do several things:

+
    +
  1. import required modules;
  2. +
  3. inherit callRemote functionality from the base +Widget class;
  4. +
  5. setup convenience attributes;
  6. +
  7. implement the doSay() method we put in our template above; +and
  8. +
  9. implement a method for updating the DOM with data it receives from +the server
  10. +
+ +
+ +

Points to note:

+
+ +

There's not much to say about the next one. This is what sets up the +relationship between our module name and the actual file itself (so that +the JavaScript can be loaded):

+ + + +

Writing the Server

+ +

Despite what one might think, writing the server may be the easiest +part! If you've created Nevow applications before, then this will look +very familiar. The only method we need is one that will send data back to +the client. Besides importing the necessary modules and creating a class +with some boilerplate, that's about it. +

+ +

Let's take a look at the code:

+ +
+ +

As promised, simple as can be. We do make use of a Twisted utility that +simplifies typing the path to our template. Some very important points:

+
+ +

Putting it All Together

+ +

Now that we've got all the code in front of us, we can trace out exactly +what happens:

+
    +
  1. the user loads the resource in their browser, and the template is +rendered;
  2. +
  3. after typing a message in the input box, the user hits submit;
  4. +
  5. upon hitting submit, the client code doSay() method is +called;
  6. +
  7. doSay() makes a remote call to the Python server method +say();
  8. +
  9. the Python server receives the data when say() is called, and +then it passes that data to the client code's addText() method;
  10. +
  11. with control back in the client code and data fresh from the server, +JavaScript can now update the page's DOM with the new data, and this is +what the addText() method does;
  12. +
  13. when addText() finishes, the cycle has completed and the +browser now displays the latest data input by the user.
  14. +
+ +

The Fruits of Our Labor

+ +

Now we get to run it! This is a little different than what you may be +used to, if you have written Twisted applications in the past. We are using +the plugin architecture of Twisted and Nevow such that twistd +will publish our element in an HTTP service. To do this, we will use +twistd's athena-widget command:

+ +
+cd Nevow/doc/howto/chattutorial/part00/listings
+twistd -n athena-widget --element=echothing.echobox.EchoElement
+
+ +

If you executed this against the tutorial code on your local machine, +you can now visit localhost:8080 and start +echoing to your heart's content.

+ +

Summary

+ +

As you can see, our echo application is a toy app that doesn't do +anything very useful. However, it has provided us with a basis for learning how +to write working Athena code that lets a browser and server communicate +with each other, both sending and receiving data. As such, we now have a +solid foundation upon which we can build a functional, useful and +instructional chat application.

+ + + + diff -Naur Nevow-0.10.0/doc/howto/chattutorial/part01/index.xhtml Nevow-0.10.0-new/doc/howto/chattutorial/part01/index.xhtml --- Nevow-0.10.0/doc/howto/chattutorial/part01/index.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/chattutorial/part01/index.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,236 @@ + + + + + Nevow Athena from Scratch: Chat Application + + + +

Architecture

+ +

We'll assume that you've read all the preceding sections of this tutorial +and have just finished the "Echo" application example. As such, we don't need +to do any more "mental preparation" and can skip straight to a description of +the architecture.

+ +

Fundamentally, this is no different than our echo application: there is +a little more chatter that takes place between the client and server; +there's another object involved (a ChatRoom); and we'll have +to run the server a little differently. +

+ +

Here are the new features we want to support:

+
    +
  • login form;
  • +
  • in-memory user storage;
  • +
  • the ability to send global alerts to all users; and
  • +
  • the ability for all users to "hear" when another user speaks in the +chat room;
  • +
+ +

A general rule we can establish about our architecture is that if something +has to happen for everyone, that code needs to appear on the server side, +since it's the server that is keeping track of all users. If something is +going to happen irrespective of other users or if browser DOM manipulation +is required, then we know the client will be the recipient of the code.

+ +

As such, in the features above, the login form will be client code. The +user storage, global alerts, and "hearing" will be implemented in server +code for the data; updating the DOM with that data will be implemented in +client code.

+ +

The user experience of this application will be the following:

+
    +
  1. they will be presented with a login box (no password, only username);
  2. +
  3. upon logging in, a message will be sent to all logged in users that +this person has joined, they will see a message at the bottom of the +chat that states their login name, and the login form will be replaced with +a chat area and a text input field;
  4. +
  5. they will type text in the input field; and
  6. +
  7. the typed text will appear in the browser of every person who is +logged in.
  8. +
+ +

Building upon our previous example, our application will do the +following:

+ +
    +
  1. JavaScript client code will extract user input and send +it to our server;
  2. +
  3. Python code will receive messages from the client;
  4. +
  5. Python code will process these messages;
  6. +
  7. Python code will send messages to the all clients; and
  8. +
  9. a template file (or stan code) will be used for +presentation.
  10. +
+ +

More Coding

+ +

Presentation

+ +

The template is very similar as it was in the previous example, with +the differences being a new login box, a "logged in as" +area, and some name changes:

+ + + +

We've now got two JavaScript methods that need to be defined: +doSetUsername() and doSay(). We can also infer +from this template that elements will be hidden and shown after login +(note the presence of style="display:none" in two places). With +these observations in hand, let's proceed to the JavaScript code.

+ +

Writing the Client

+ +

Referring back to our thoughts in the "Architecture" section above, we +can establish that the JavaScript code needs the following:

+ +
    +
  • have the same basic boilerplate as in the "echo" example (imports, +inheritance, attribute-setting in the constructor);
  • +
  • implement the doSetUsername() and doSay() +methods;
  • +
  • create a method that will send a message to all users; and
  • +
  • create a method that will let everyone know when someone says +something. Let's see how this is done.
  • +
+ +
+ +

There is a little abstraction here:

+
    +
  • we need a general message-sending method (displayMessage()) for any +message that gets sent to all users;
  • +
  • for user chat messages, we need something that will prepend the username so +that everyone knows who said what (displayUserMessage()), and once this method +does its thing, it passes the adjusted message on to displayMessage().
  • +
+ +

Other than that, this is very straight-forward code; it's pretty much +the same as the "Echo" tutorial. The display*() methods +are only responsible for updating the UI, just as we would expect.

+ +We also need the same glue that we demonstrated in the "Echo" example: + +
+ +

Writing the Server

+ +

The server code is a bit more complicated. We +anticipated this above in the "Architecture" section where we noted that +the Python code needs to receive, process and send messages.

+ +
+ +

There is something in our "Chat" code that is not at all present in the +"Echo" application: the ChatRoom object. We need this object for the +following functionality:

+
    +
  • a means of instantiating new ChatterElement clients;
  • +
  • a "singleton" instance for keeping track of all ChatterElement clients;
  • +
  • a means sending messages to all clients;
  • +
+ +

Let's look at the second two reasons first. In our "Chat" application, +a new ChatterElement is created whenever a user connects, +so we will have potentially many of these instances. In order +for our chat server to function as designed, it will need a way to +communicate with each of these. If we create an object that can keep the +ChatterElementes in a list, then it will be able to iterate that +list and call methods that, in turn, make remote calls to the JavaScript. +

+ +

Because we need the chat room to be a singleton object, it +can only be instantiated once. But we need many instantiations of +ChatterElement -- one for each connection, in fact. So what do +we do? Well, in this case, we make one of the methods +of ChatRoom a factory for instantiating a +ChatterElement. Before we return the instance, though, we +append it to the list of instances that the ChatRoom +is keeping track of. +

+ +

Putting it All Together

+ + +

Now that we've got all the code in front of us, we can trace out exactly what happens:

+ +
    +
  1. the user loads the resource in their browser, and the template is rendered;
  2. +
  3. after typing a message in the input box, the user hits submit;
  4. +
  5. JavaScript client code calls to the server with the text the user submitted;
  6. +
  7. the server gets the message and shares it with all the connected +ChatterElements;
  8. +
  9. each ChatterElement hears this message and passes it back to the JavaScript client;
  10. +
  11. the client prepends the username to the message and then updates the display with the complete message.
  12. +
+ +

+Keep in mind that ChatterElement entails several duties: it +establishes a relationship with a room object, it "registers" a user (there's a +one-to-one mapping between users and ChatterElement), it sends +messages to the browser, and it receives messages from the chat room. Being a +LiveElement subclass, ChatterElement is also +responsible for the view (via the document factory). +

+ + +

Running with twistd

+ +

One last bit of code that may seem odd is the chat +variable we define right after the ChatRoom class. What +is this? This is how we make all this cleverness work as a twisted +plugin.

+ +

If you recall, in our "Echo" application, we ran the code with +the following command: +

+ +
+twistd -n athena-widget --element=echothing.echobox.EchoElement
+
+ +

The value we pass as the --element argument is the dotted +name of the LiveElement object of which our "web page" +is primarily comprised: the EchoElement object. In +our "Chat" application, we have more moving parts: not only +do we have the ChatterElement object, but we have the +ChatRoom object which is responsible for keeping track of +many ChatterElementes. By defining the chat +variable, we are accomplishing the following all at once: +

+ +
    +
  • providing a variable that can be accessed as a dotted name and thus +used when starting the server (chatthing.chatterbox.chat);
  • +
  • creating a singleton of ChatRoom (via the "magic" +of Python module-level instantiations);
  • +
  • making use of a factory, that when called, will both return a +new ChatterElement instance and add itself to the +ChatRoom.
  • +
+ +

Running this version of our code is a little bit different than the +"Echo" version. This is because of the ChatRoom code we +discussed above. As such, we pass a factory as our element, like so:

+ +
+cd Nevow/doc/howto/chattutorial/part01/listings
+twistd -n athena-widget --element=chatthing.chatterbox.chat
+
+ +

If you executed this against the tutorial code on your local machine, +you can now visit http://localhost:8080/ +and start chatting to your heart's content.

+ +

Summary

+

+Unlike our echo application, the chat application has some real functionality +and does some useful stuff: supporting user chats via browser/server two-way +communications. It should be evident now how the echo application provided a +basic conceptual and (partially) functional foundation upon which our chat work +could be based. +

+ + diff -Naur Nevow-0.10.0/doc/howto/deployment.xhtml Nevow-0.10.0-new/doc/howto/deployment.xhtml --- Nevow-0.10.0/doc/howto/deployment.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/deployment.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,300 @@ + + + + + + + Deployment + + + +

Deployment

+ +

+ Nevow includes two major phases for deciding what HTML to render. Object Traversal is the procedure by which a + URL is mapped to a Python object which will perform the HTML + generation. Page Rendering is the process by + which data objects are combined with an HTML template to produce the + final output. +

+ +

+ Before any of this can take place, however, we must have an environment + in which our Python code can run in response to an HTTP request, and HTML + can be returned to the browser for rendering. This is called the + Deployment Environment. +

+ +

+ There are various deployment options for Nevow page code: +

+ +
    +
  • + CGI: Simple deployment in almost any HTTP server +
  • +
  • + WSGI: A more complete and flexible way for deploying on many HTTP + servers +
  • +
  • + Twisted.Web: A standalone application server process which includes a + built-in HTTP server +
  • +
  • + Zomne: A small CGI which hands off HTTP requests to a long-running + application server process, similar to FastCGI or SCGI +
  • +
+ +

CGI

+ +

+ You can deploy Nevow on any webserver which uses the Common Gateway + Interface. Using this method, your code is responsible for properly + formatting and outputting the HTTP response headers, and Nevow is used + only to generate the HTML body of your page. Here is the simplest + possible CGI: +

+ +
+#!/usr/bin/env python
+
+print "Content-type: text/plain\r\n\r\n",
+
+from nevow import rend, loaders
+
+class HelloWorld(rend.Page):
+    docFactory = loaders.stan("Hello, world!")
+
+print HelloWorld().renderSynchronously()
+    
+ +

+ With this simple CGI you can use the Nevow template loaders and standard + nevow template interpolation techniques in your CGIs. However, you do not + get any Object Traversal features, and you + have to generate HTTP headers yourself. WSGI is a slightly higher-level + deployment option which does not suffer these problems. +

+ +

WSGI

+ +

+ WSGI is a python interface for plugging web applications into various + HTTP server architectures. It is described in PEP 333, the Python + Web Services Gateway Interface Python Enhancement Proposal. Nevow + includes the nevow.wsgi module, which includes a + createWSGIApplication function which takes a Page and + returns a standard WSGI application callable. With the help of the + run_with_cgi example gateway from the PEP (which I will omit + here), our CGI example becomes shorter: +

+ +
+#!/usr/bin/env python
+
+from nevow import rend, loaders, wsgi
+
+class HelloWorld(rend.Page):
+    docFactory = loaders.stan("Hello, world!")
+
+run_with_cgi(wsgi.createWSGIApplication(HelloWorld()))
+    
+ +

+ Of course, you can use any available WSGI gateway to publish your + application object, such as one of the gateways which comes with the PEAK toolkit. For example, here + is a simple python module which creates a WSGI application which we will + then deploy with PEAK's SimpleHTTPServer gateway:: +

+ +
+## helloworld.py
+
+from nevow import rend, loaders, wsgi
+
+class HelloWorld(rend.Page):
+    docFactory = loaders.stan("Hello, world!")
+
+application = wsgi.createWSGIApplication(HelloWorld())
+    
+ +

+ Save this file as "helloworld.py" somewhere on your PYTHONPATH and then + run the following command: +

+ +
peak launch WSGI import:helloworld.application
+ +

+ This will bring up a SimpleHTTPServer running your Nevow code and launch + a web browser to view the output. (TODO: I couldn't get this working + immediately but I will seek assistance with PEAK and update the + instructions once I do.) +

+ +

Twisted.Web

+ +

+ A convenient and powerful way to deploy Nevow applications is inside a + process running the twisted.web HTTP server. With Python, Twisted, and + Nevow installed, you have all you need to run a Web Application, with no + other dependencies or external HTTP servers such as Apache + required. Running your Nevow applications under twisted.web also gives + you access to some of the more advanced "Live" features of Nevow, such as + nevow.livepage and nevow.canvas. Currently, + these modules require more control over the HTTP socket than CGI or WSGI + can provide. (This may change in the future.) +

+ +

+ Deploying a Nevow application under twisted.web requires a little more + boilerplate, but can be considerably easier to set up than other + deployment options because there are no external dependencies. Note that + normally you should declare your Page classes in modules external to the + twisted configuration file, but everything is included in one file here + for brevity. Here is the minimal configuration file required to use + Nevow with twisted.web: +

+ +
+from nevow import rend, loaders, appserver
+
+class HelloWorld(rend.Page):
+    docFactory = loaders.stan("Hello, world!")
+
+from twisted.application import service, internet
+application = service.Application("hello-world")
+internet.TCPServer(8080, appserver.NevowSite(HelloWorld())).setServiceParent(application)
+    
+ +

+ Save this file as "helloworld.tac" and start the server using the + command: +

+ +
twistd -noy helloworld.tac
+ +

+ Then visit your twisted.web server by viewing the url + "http://localhost:8080/" in your browser. See the twistd man page for + more information about what twistd is capable of, including daemonizing + the HTTP server. +

+ +

Zomne

+ +

+ Warning Zomne is experimental. It may blow up your computer and + require your first born son as a sacrifice. Zomne also only works in + UNIX-like environments where unix domain sockets are available, and may + not work on windows. +

+ +

+ Zomne, or "Zombie Nevow", is a CGI written in C which can start up a + long-running Application Server process if one is not already running. It + then uses a simple custom protocol to transmit information about the HTTP + request from the CGI process to the application server process. +

+ +

+ Zomne combines the ease of deployment of the CGI environment with the + speed and flexibility of the twisted.web long-running application server + process model. +

+ +

+ To use Zomne, you must first compile the CGI. cd into the directory + created when unpacking the Nevow tarball, and compile the CGI: +

+ +
% gcc zomne.c
+ +

+ Move it into your cgi-bin: +

+ +
% mv a.out /Library/WebServer/CGI-Executables/nevow.cgi
+ +

+ Create a file which tells the cgi where to look for the application: +

+ +
+% cat > /Library/WebServer/CGI-Executables/.nevow.cgi.dir
+/Users/dp/zomne-test
+^D
+ +

+ The CGI name can be anything, as long as there is a file with a prepended + "." and a postfixed ".dir" in the same directory which contains the full + path of a zomne application directory. Next, create the application + directory: +

+ +
mkdir /Users/dp/zomne-test
+ +

+ Finally, create the zomne.tac file which the zomne.cgi will execute to + start the long-running application server process: +

+ +
+from nevow import rend, loaders, zomnesrv
+
+class HelloWorld(rend.Page):
+    docFactory = loaders.stan("Hello, world!")
+
+from twisted.application import service, internet
+application = service.Application('nevow-zomne-test')
+internet.UNIXServer('zomne.socket', zomnesrv.ZomneFactory(HelloWorld())).setServiceParent(application)
+    
+ +

+ Now, visiting the nevow.cgi URL through the web should render the Hello + World page, after a pause while the server is starting up. Subsequent + requests should be very fast, because the application server is already + running, and the CGI merely has to forward the request to it. +

+ +

+ Another useful capability of the zomne CGI process is the ability to + control environment variables the CGI will use. Create a directory named + "zomne_environ" in the application directory, and fill it with text files + whose name will be the environment key and whose contents will be the + environment value: +

+ +
+% cd zomne-test
+% mkdir zomne-environ
+% cd zomne-environ
+% cat > PYTHONPATH
+/Users/dp/Projects/Nevow:/Users/dp/Projects/helloworld
+^D
+ +

Conclusion

+ +

+ Nevow may be deployed in a number of environments, from the most + restrictive to the most permissive. Writing a CGI can be an easy way to + try out the Nevow templating mechanism, but can be slow. A long-running + application server process can be a good way to get good performance as + well as additional features such as in-memory server-side sessions, + advanced automatic form handling with formless, and live page updating + features such as nevow.livepage and nevow.canvas. +

+ +

+ Which deployment option you choose will depend on the amount of control + you have over your deployment environment, and what advanced features + your application will require. +

+ + diff -Naur Nevow-0.10.0/doc/howto/gettingstarted.xhtml Nevow-0.10.0-new/doc/howto/gettingstarted.xhtml --- Nevow-0.10.0/doc/howto/gettingstarted.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/gettingstarted.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,110 @@ + + + + + + + Getting Started + + + +

Getting Started

+ +

+ Warning: This document has only just been started. It's not going to get + you very far right now. +

+ +

+ Nevow is a reasonably large library and can be quite daunting at + first. This document's aim is to guide the first time user in building a + Nevow application. +

+ +

Our First Application

+ +

+ Let's dive straight in, here's the code for our first (very, very simple) + application. Create the following module, helloworld.py: +

+ + + helloworld.py + + +

+ It looks quite simple but let's walk through it anyway. +

+ +

+ First, we import two Nevow modules. nevow.loaders contains template loaders of which the + two most useful are xmlfile + and stan. + xmlfile can load any well-formed XML (i.e. XHTML) file; + stan loads a stan tree (more on these later). The other + module, nevow.rend, contains all Nevow's + standard renders, many of which we'll meet in this document. +

+ +

+ We then define the HelloWorld class that subclasses rend.Page, Nevow's main resource + class. HelloWorld has two class + attributes. addSlash tells rend.Page to + redirect to a version of the request URL that ends in a / if + necessary. You generally want to set this to True for the + root resource. docFactory tells the page instance where to + get the template from. In this case we're providing a loader that parses + an HTML file (not shown) from disk. +

+ +

+ Hmm, ok I hear you say but how do I see it. Well, Twisted provides a good + web server which we can use. Twisted also includes a clever little + application for starting Twisted applications. Here's the helloworld.tac + file, a Twisted Application Configuration: +

+ + + helloworld.tac + + +

+ Give it a go, run the following and connect to http://localhost:8080/ to see your + application: +

+ +
twistd -ny helloworld.tac
+ +

+ You'll probably notice that you get log output on the console. This is + just one of the good things that twistd does. It can also daemonize the + application, shed privileges if run as root, etc. +

+ +

+ TAC files are covered in more detail in the Twisted documentation but + let's quickly explain what all this does anyway. +

+ +

+ When twistd starts up it loads the + .tac file (it's just Python) and looks for the attribute + called application. When twistd + is all ready to go it starts the application. +

+ +

+ The application is not much use unless it actually does something so the + next thing we do is create a NevowSite instance, site, and + pass it a root resource, a HelloWorld instance. Finally, we + create a TCP server that makes the site available on port 8080 and bind + the server to the application to ensure the server is started when the + application is started. +

+ + diff -Naur Nevow-0.10.0/doc/howto/glossary.xhtml Nevow-0.10.0-new/doc/howto/glossary.xhtml --- Nevow-0.10.0/doc/howto/glossary.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/glossary.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,82 @@ + + + + + + + Glossary + + + +

Glossary

+ +

Object Traversal

+ +

+ The process by which a Python object is located to render HTML for a + given HTTP URL. For example, given the URL http://example.com/foo/bar, + Object Traversal will begin at the "Root Resource" object by asking it + for an object which is capable of rendering the page at ('foo', + 'bar'). The "Root Resource" returns an object and a list of unhandled + path segments, and the traversal continues across this new Resource + object until all path segments have been consumed. +

+ +

Page Rendering

+ +

+ The process by which a Python object, usually a rend.Page subclass, turns + itself into HTML. Page Rendering involves locating some page data, + loading a template document, and applying the template to the data, in + the process generating HTML. +

+ +

Deployment Environment

+ +

+ The environment in which a Nevow application is deployed. Generally + involves an HTTP server which is configured to route certain (or all) + HTTP requests through the Nevow Object Traversal and Page Rendering + process. Deployment environments include CGI, WSGI, and twisted.web. +

+ +

DOM

+ +

+ Document Object Model. A tree of objects which represent the structure of + an XHTML document in memory. Nevow uses a nonstandard DOM named "stan", + which is made up of simple Python lists, dicts, strings, and + nevow.stan.Tag instances. +

+ +

Flattener

+ +

+ A Python function which knows how to translate from a rich type to a + string containing HTML. For example, the integer flattener calls str() on + the integer. The string flattener escapes characters which are unsafe in + HTML, such as <, >, and &. +

+ +

Tag

+ +

+ A class, defined at nevow.stan.Tag, which holds information about a + single HTML tag in a DOM. Tag instances have three attributes: tagName, + attributes, and children. tagName is a string indicating the tag + name. attributes is a dict indicating the HTML attributes of that + node. children is a list indicating the child nodes of that node. +

+ +

Tag Specials

+ +

+ A Tag attribute which is "special" to nevow. Tag specials include data, + render, pattern, slot, and macro. Tag Specials will never be output as + HTML attributes of tags, but will be used by the internal Nevow rendering + process to influence how the Tag is rendered. +

+ + + diff -Naur Nevow-0.10.0/doc/howto/index.xhtml Nevow-0.10.0-new/doc/howto/index.xhtml --- Nevow-0.10.0/doc/howto/index.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/index.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,49 @@ + + + + + + + Manual + + + +

+ Manual +

+
    +
  • + Introduction +
  • +
  • + Getting Started +

    A basic introduction to rendering a web page in Nevow.

    +
  • +
  • + Object Traversal +

    Getting from an URL to a Python page object you want to render.

    +
  • +
  • + Object Publishing +

    Exposing Python objects as parts of a web page in Nevow.

    +
  • +
  • + XML Templates +

    Using standard XHTML as a template for Nevow.

    +
  • +
  • + Deployment +

    How to get your Nevow application running on different types of + servers.

    +
  • +
  • + Nevow Athena +

    Two-way communication with JavaScript in a web browser.

    +
  • +
  • + Glossary +
  • +
+ + diff -Naur Nevow-0.10.0/doc/howto/intro.xhtml Nevow-0.10.0-new/doc/howto/intro.xhtml --- Nevow-0.10.0/doc/howto/intro.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/intro.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,294 @@ + + + + + + + A Web Application Construction Kit + + + +

A Web Application Construction Kit

+ +

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 +
  • +
  • + formless.webform: 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 xmlns:nevow="http://nevow.com/ns/nevow/0.1">
+  <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(rend.Page):
+    docFactory = loaders.xmlfile("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 xmlns:nevow="http://nevow.com/ns/nevow/0.1">
+  <body>
+    <span nevow:data="name" nevow:render="colorful" />
+    <span nevow:data="fun" nevow:render="colorful" />
+  </body>
+</html>
+ +

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

+ +
+class Colorful(rend.Page):
+    docFactory = loaders.xmlfile("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 rend, tags
+
+class Greeter(rend.Page):
+    def greet(self, context, data):
+        return random.choice(["Hello", "Greetings", "Hi"]), " ", data
+
+    docFactory = loaders.stan(
+        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 zope.interface import implements
+from formless.annotate 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)
+    
+ +

Webform

+ +

+ Webform 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 rend, tags
+from formless import webform
+
+class WebForm(rend.Page):
+    document = rend.stan(
+    tags.html[
+    tags.body[
+        h1["Here is the form:"],
+        webform.renderForms('original')
+    ]
+])
+
+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. New for + Nevow 0.3, LivePage has been updated to support Mozilla, Firefox, IE6 + Win, and Safari. Using LivePage is very easy: +

+ +
+from nevow.liveevil import handler
+
+def greeter(client, nodeName):
+    client.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 = handler(greeter, 'node.name')
+
+class Live(rend.Page):
+    docFactory = loaders.stan(
+        tags.html[
+        tags.body[
+            ol[
+                li(onclick=handler, name="one")["One"]
+                li(onclick=handler, name="two")["Two"]
+                li(onclick=handler, name="three")["Three"]
+            ]
+        ]
+    ])
+    
+ +

More Information

+ +

+ The Nevow website + has more information. Starting with 0.3, it contains a simple WSGI + implementation and can also be used to render CGIs. However, the + recommended mode of operation is using the Twisted web + server. Nevow is an active project, and many new bugfixes and features + are committed to the Nevow SVN repository. Information about Nevow + commits is available by subscribing to the + Divmod commits mailing list. The Nevow SVN repository can be checked + out using: +

+ +
svn co svn://divmod.org/svn/Nevow/trunk Nevow
+ +

+ Discussion of Nevow occurs on the + twisted.web mailing list. The Nevow developers are also often + available for real-time help on the #twisted.web channel on + irc.freenode.net. +

+ + diff -Naur Nevow-0.10.0/doc/howto/publishing.xhtml Nevow-0.10.0-new/doc/howto/publishing.xhtml --- Nevow-0.10.0/doc/howto/publishing.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/publishing.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,658 @@ + + + + + + + Object Publishing + + + +

+ Object Publishing +

+ +

+ In Object Traversal, we learned about the + nevow.inevow.IResource.renderHTTP method, which + is the most basic way to send HTML to a browser when using + Nevow. However, it is not very convenient (or clean) to generate HTML + tags by concatenating strings in Python code. In the Deployment documentation, we saw that it was + possible to render a Hello World page using a + nevow.rend.Page subclass and providing a docFactory: +

+ +
+>>> from nevow import rend, loaders
+>>> class HelloWorld(rend.Page):
+...     docFactory = loaders.stan("Hello, world!")
+...
+>>> HelloWorld().renderSynchronously()
+'Hello, world!'
+ +

+ This example does nothing interesting, but the concept of a loader is + important in Nevow. The rend.Page.renderHTTP implementation + always starts rendering HTML by loading a template from the + docFactory. +

+ + +

The stan DOM

+ +

+ Nevow uses a DOM-based approach to rendering HTML. A tree of objects is + first constructed in memory by the template loader. This tree is then + processed one node at a time, applying functions which transform from + various Python types to HTML strings. +

+ +

+ Nevow uses a nonstandard DOM named "stan". Unlike the W3C DOM, stan is + made up of simple python lists, strings, and instances of the + nevow.stan.Tag class. During the rendering process, "Flattener" + functions convert from rich types to HTML strings. For example, we can + load a template made up of some nested lists and Python types, render it, + and see what happens: +

+ +
+>>> class PythonTypes(rend.Page):
+...     docFactory = loaders.stan(["Hello", 1, 1.5, True, ["Goodbye", 3]])
+...
+>>> PythonTypes().renderSynchronously()
+'Hello11.5TrueGoodbye3'
+ +

Tag instances

+ +

+ So far, we have only rendered simple strings as output. However, the main + purpose of Nevow is HTML generation. In the stan DOM, HTML tags are + represented by instances of the nevow.stan.Tag + class. Tag is a very simple class, whose instances have an + attributes dictionary and a children list. The + Tag flattener knows how to recursively flatten attributes + and children of the tag. To show you how Tags really work + before you layer Nevow's convenience syntax on top, try this horrible + example: +

+ +
+>>> from nevow import stan
+>>> h = stan.Tag('html')
+>>> d = stan.Tag('div')
+>>> d.attributes['style'] = 'border: 1px solid black'
+>>> h.children.append(d)
+>>> class Tags(rend.Page):
+...     docFactory = loaders.stan(h)
+...
+>>> Tags().renderSynchronously()
+'<html><div style="border: 1px solid black"></div></html>'
+ +

+ So, we see how it is possible to programatically generate HTML by + constructing and nesting stan Tag instances. However, it is + far more convenient to use the overloaded operators Tag + provides to manipulate them. Tag implements a + __call__ method which takes any keyword arguments and values + and updates the attributes dictionary; it also implements a + __getitem__ method which takes whatever is between the square + brackets and appends them to the children list. A simple example should + clarify things: +

+ +
+>>> class Tags2(rend.Page):
+...     docFactory = loaders.stan(stan.Tag('html')[stan.Tag('div')(style="border: 1px solid black")])
+...
+>>> Tags2().renderSynchronously()
+'<html><div style="border: 1px solid black"></div></html>'
+ +

+ This isn't very easy to read, but luckily we can simplify the example + even further by using the nevow.tags module, which is full of "Tag + prototypes" for every tag type described by the XHTML 1.0 specification: +

+ +
+>>> class Tags3(rend.Page):
+...     docFactory = loaders.stan(tags.html[tags.div(style="border: 1px solid black")])
+...
+>>> Tags3().renderSynchronously()
+'<html><div style="border: 1px solid black"></div></html>'
+ +

+ Using stan syntax is not the only way to construct template DOM for use + by the Nevow rendering process. Nevow also includes loaders.xmlfile which implements a simple tag + attribute language similar to the Zope Page Templates (ZPT) Tag Attribute + Language (TAL). However, experience with the stan DOM should give you + insight into how the Nevow rendering process really works. Rendering a + template into HTML in Nevow is really nothing more than iterating a tree + of objects and recursively applying "Flattener" functions to objects in + this tree, until all HTML has been generated. +

+ +

Functions in the DOM

+ +

+ So far, all of our examples have generated static HTML pages, which is + not terribly interesting when discussing dynamic web applications. Nevow + takes a very simple approach to dynamic HTML generation. If you put a + Python function reference in the DOM, Nevow will call it when the page is + rendered. The return value of the function replaces the function itself + in the DOM, and the results are flattened further. This makes it easy to + express looping and branching structures in Nevow, because normal Python + looping and branching constructs are used to do the job: +

+ +
+>>> def repeat(ctx, data):
+...     return [tags.div(style="color: %s" % (color, ))
+...         for color in ['red', 'blue', 'green']]
+...
+>>> class Repeat(rend.Page):
+...     docFactory = loaders.stan(tags.html[repeat])
+...
+>>> Repeat().renderSynchronously()
+'<html><div style="color: red"></div><div style="color: blue"></div><div style="color: green"></div></html>'
+ +

+ However, in the example above, the repeat function isn't even necessary, + because we could have inlined the list comprehension right where we + placed the function reference in the DOM. Things only really become + interesting when we begin writing parameterized render functions which + cause templates to render differently depending on the input to the web + application. +

+ +

+ The required signature of functions which we can place in the DOM is + (ctx, data). The "context" object is essentially opaque for now, and we + will learn how to extract useful information out of it later. The "data" + object is anything we want it to be, and can change during the rendering + of the page. By default, the data object is whatever we pass as the first + argument to the Page constructor, or the Page instance itself if + nothing is passed. Armed with this knowledge, we can create a Page which + renders differently depending on the data we pass to the Page + constructor: +

+ +
+class Root(rend.Page):
+    docFactory = loaders.stan(tags.html[
+        tags.h1["Welcome."],
+        tags.a(href="foo")["Foo"],
+        tags.a(href="bar")["Bar"],
+        tags.a(href="baz")["Baz"]])
+
+    def childFactory(self, ctx, name):
+        return Leaf(name)
+
+def greet(ctx, name):
+    return "Hello. You are visiting the ", name, " page."
+
+class Leaf(rend.Page):
+    docFactory = loaders.stan(tags.html[greet])
+    
+ +

+ Armed with this knowledge and the information in the Object Traversal documentation, we now have + enough information to create dynamic websites with arbitrary URL + hierarchies whose pages render dynamically depending on which URL was + used to access them. +

+ +

Accessing query parameters and form post data

+ +

+ Before we move on to more advanced rendering techniques, let us first + examine how one could further customize the rendering of a Page based on + the URL query parameters and form post information provided to us by a + browser. Recall that URL parameters are expressed in the form: +

+ +
http://example.com/foo/bar?baz=1&quux=2
+ +

+ And form post data can be generated by providing a form to a browser: +

+ +
+<form action="" method="POST">
+  <input type="text" name="baz" />
+  <input type="text" name="quux" />
+  <input type="submit" />
+</form>
+ +

+ Accessing this information is such a common procedure that Nevow provides + a convenience method on the context to do it. Let's examine a simple page + whose output can be influenced by the query parameters in the URL used to + access it: +

+ +
+def showChoice(ctx, data):
+    choice = ctx.arg('choice')
+    if choice is None:
+        return ''
+    return "You chose ", choice, "."
+
+class Custom(rend.Page):
+    docFactory = loaders.stan(tags.html[
+        tags.a(href="?choice=baz")["Baz"],
+        tags.a(href="?choice=quux")["Quux"],
+        tags.p[showChoice]])
+    
+ +

+ The procedure is exactly the same for simple form post information: +

+ +
+def greet(ctx, data):
+    name = ctx.arg('name')
+    if name is None:
+        return ''
+    return "Greetings, ", name, "!"
+
+class Form(rend.Page):
+    docFactory = loaders.stan(tags.html[
+        tags.form(action="", method="POST")[
+            tags.input(name="name"),
+            tags.input(type="submit")],
+        greet])
+
+ +

+ Note that ctx.arg returns only the first argument with the + given name. For complex cases where multiple arguments and lists of + argument values are required, you can access the request argument + dictionary directly using the syntax: +

+ +
+def arguments(ctx, data):
+    args = inevow.IRequest(ctx).args
+    return "Request arguments are: ", str(args)
+    
+ +

Generators in the DOM

+ +

+ One common operation when building dynamic pages is iterating a list of + data and emitting some HTML for each item. Python generators are well + suited for expressing this sort of logic, and code which is written as a + python generator can perform tests (if) and loops of various + kinds (while, for) and emit a row of html + whenever it has enough data to do so. Nevow can handle generators in the + DOM just as gracefully as it can handle anything else: +

+ +
+>>> from nevow import rend, loaders, tags
+>>> def generate(ctx, items):
+...     for item in items:
+...         yield tags.div[ item ]
+...
+>>> class List(rend.Page):
+...     docFactory = loaders.stan(tags.html[ generate ])
+...
+>>> List(['one', 'two', 'three']).renderSynchronously()
+'<html><div>one</div><div>two</div><div>three</div></html>'
+ +

+ As you can see, generating HTML inside of functions or generators can be + very convenient, and can lead to very rapid application + development. However, it is also what I would call a "template + abstraction violation", and we will learn how we can keep knowledge of + HTML out of our python code when we learn about patterns and slots. +

+ +

Methods in the DOM

+ +

+ Up until now, we have been placing our template manipulation logic inside + of simple Python functions and generators. However, it is often + appropriate to use a method instead of a function. Nevow makes it just as + easy to use a method to render HTML: +

+ +
+class MethodRender(rend.Page):
+    def __init__(self, foo):
+        self.foo = foo
+
+    def render_foo(self, ctx, data):
+        return self.foo
+
+    docFactory = loaders.stan(tags.html[ render_foo ])
+    
+ +

+ Using render methods makes it possible to parameterize your Page class + with more parameters. With render methods, you can also use the Page + instance as a state machine to keep track of the state of the + render. While Nevow is designed to allow you to render the same Page + instance repeatedly, it can also be convenient to know that a Page + instance will only be used one time, and that the Page instance can be + used as a scratch pad to manage information about the render. +

+ +

Data specials

+ +

+ Previously we saw how passing a parameter to the default Page constructor + makes it available as the "data" parameter to all of our render + methods. This "data" parameter can change as the page render proceeds, + and is a useful way to ensure that render functions are isolated and only + act upon the data which is available to them. Render functions which do + not pull information from sources other than the "data" parameter are + more easily reusable and can be composed into larger parts more easily. +

+ +

+ Deciding which data gets passed as the data parameter is as simple as + changing the "Data special" for a Tag. See the Glossary under "Tag Specials" for more + information about specials. Assigning to the data special is as simple as + assigning to a tag attribute: +

+ +
+>>> def hello(ctx, name):
+...     return "Hello, ", name
+...
+>>> class DataSpecial(rend.Page):
+...     docFactory = loaders.stan(tags.html[
+...     tags.div(data="foo")[ hello ],
+...     tags.div(data="bar")[ hello ]])
+...
+>>> DataSpecial().renderSynchronously()
+'<html><div>Hello, foo</div><div>Hello, bar</div></html>'
+ +

+ Data specials may be assigned any python value. Data specials are only in + scope during the rendering of the tag they are assigned to, so if the + "hello" renderer were placed in the DOM inside the html node directly, + "Hello, None" would be output. +

+ +

+ Before data is passed to a render function, Nevow first checks to see if + there is an IGettable adapter for it. If there + is, it calls IGettable.get(), and passes the result of this + as the data parameter instead. Nevow includes an IGettable + adapter for python functions, which means you can set a Tag data special + to a function reference and Nevow will call it to obtain the data when + the Tag is rendered. The signature for data methods is similar to that of + render methods, (ctx, data). For example: +

+ +
+def getName(ctx, data):
+    return ctx.arg('name')
+
+def greet(ctx, name):
+    return "Greetings, ", name
+
+class GreetName(rend.Page):
+    docFactory = loaders.stan(tags.html[
+        tags.form(action="")[
+            tags.input(name="name"),
+            tags.input(type="submit")],
+            tags.div(data=getName)[ greet ]])
+    
+ +

+ Data specials exist mainly to allow you to construct and enforce a + Model-View-Controller style separation of the Model code from the + View. Here we see that the greet function is capable of rendering a + greeting view for a name model, and that the implementation of getName + may change without the view code changing. +

+ +

Render specials

+ +

+ Previously, we have seen how render functions can be placed directly in + the DOM, and the return value replaces the render function in the + DOM. However, these free functions and methods are devoid of any + contextual information about the template they are living in. The + render special is a way to associate a render function or method with a + particular Tag instance, which the render function can then examine to + decide how to render: +

+ +
+>>> def alignment(ctx, data):
+...     align = ctx.tag.attributes.get('align')
+...     if align == 'right':
+...         return ctx.tag["Aligned right"]
+...     elif align == 'center':
+...         return ctx.tag["Aligned center"]
+...     else:
+...         return ctx.tag["Aligned left"]
+...
+>>> class AlignmentPage(rend.Page):
+...     docFactory = loaders.stan(tags.html[
+...     tags.p(render=alignment),
+...     tags.p(render=alignment, align="center"),
+...     tags.p(render=alignment, align="right")])
+...
+>>> AlignmentPage().renderSynchronously()
+'<html><p>Aligned left</p><p align="center">Aligned center</p><p align="right">Aligned right</p></html>'
+ +

+ Note how the alignment renderer has access to the template node as + ctx.tag. It can examine and change this node, and the return value of + the render function replaces the original node in the DOM. Note that + here we are returning the template node after changing it. We will see + later how we can instead mutate the context and use slots so that the + knowledge the renderer requires about the structure of the template is + reduced even more. +

+ +

Pattern specials

+ +

+ When writing render methods, it is easy to inline the construction of + Tag instances to generate HTML programatically. However, this creates a + template abstraction violation, where part of the HTML which will show + up in the final page output is hidden away inside of render methods + instead of inside the template. Pattern specials are designed to avoid + this problem. A node which has been tagged with a pattern special can + then be located and copied by a render method. The render method does + not need to know anything about the structure or location of the + pattern, only it's name. +

+ +

+ We can rewrite our previous generator example so that the generator + does not have to know what type of tag the template designer would like + repeated for each item in the list: +

+ +
+>>> from nevow import rend, loaders, tags, inevow
+>>> def generate(ctx, items):
+...     pat = inevow.IQ(ctx).patternGenerator('item')
+...     for item in items:
+...         ctx.tag[ pat(data=item) ]
+...     return ctx.tag
+...
+>>> def string(ctx, item):
+...     return ctx.tag[ str(item) ]
+...
+>>> class List(rend.Page):
+...     docFactory = loaders.stan(tags.html[
+...     tags.ul(render=generate)[
+...         tags.li(pattern="item", render=string)]])
+...
+>>> List([1, 2, 3]).renderSynchronously()
+'<html><ol><li>1</li><li>2</li><li>3</li></ol></html>'
+ +

+ Note that we have to mutate the tag in place and repeatedly copy the + item pattern, applying the item as the data special to the resulting + Tag. It turns out that this is such a common operation that nevow comes + out of the box with these two render functions: +

+ +
+>>> class List(rend.Page):
+...     docFactory = loaders.stan(tags.html[
+...     tags.ul(render=rend.sequence)[
+...         tags.li(pattern="item", render=rend.data)]])
+...
+>>> List([1, 2, 3]).renderSynchronously()
+'<html><ul><li>1</li><li>2</li><li>3</li></ul></html>'
+ +

Slot specials

+ +

+ The problem with render methods is that they are only capable of making + changes to their direct children. Because of the architecture of Nevow, + they should not attempt to change grandchildren or parent nodes. It is + possible to write one render method for every node you wish to change, + but there is a better way. A node with a slot special can be "filled" + with content by any renderer above the slot. Creating a slot special is + such a frequent task that there is a prototype in nevow.tags + which is usually used. +

+ +

+ Let us examine a renderer which fills a template with information about + a person: +

+ +
+>>> from nevow import loaders, rend, tags
+...
+>>> person = ('Donovan', 'Preston', 'Male', 'California')
+...
+>>> def render_person(ctx, person):
+...     firstName, lastName, sex, location = person
+...     ctx.fillSlots('firstName', firstName)
+...     ctx.fillSlots('lastName', lastName)
+...     ctx.fillSlots('sex', sex)
+...     ctx.fillSlots('location', location)
+...     return ctx.tag
+...
+>>> class PersonPage(rend.Page):
+...     docFactory = loaders.stan(tags.html(render=render_person)[
+...     tags.table[
+...         tags.tr[
+...             tags.td[tags.slot('firstName')],
+...             tags.td[tags.slot('lastName')],
+...             tags.td[tags.slot('sex')],
+...             tags.td[tags.slot('location')]]]])
+...
+>>> PersonPage(person).renderSynchronously()
+'<html><table><tr><td>Donovan</td><td>Preston</td><td>Male</td><td>California</td></tr></table></html>'
+ +

+ Using patterns in combination with slots can lead to very powerful + template abstraction. Nevow also includes another standard renderer + called "mapping" which takes any data which responds to the "items()" + message and inserts the items into appropriate slots: +

+ +
+>>> class DictPage(rend.Page):
+...     docFactory = loaders.stan(tags.html(render=rend.mapping)[
+...         tags.span[ tags.slot('foo') ], tags.span[ tags.slot('bar') ]])
+...
+>>> DictPage(dict(foo=1, bar=2)).renderSynchronously()
+'<html><span>1</span><span>2</span></html>'
+ +

Data directives

+ +

+ So far, we have always placed data functions directly in the Data + special attribute of a Tag. Sometimes, it is preferable to look up a + data method from the Page class as the Page has being rendered. For + example, a base class may define a template and a subclass may provide + the implementation of the data method. We can accomplish this effect by + using a data directive as a Tag's data special: +

+ +
+class Base(rend.Page):
+    docFactory = loaders.stan(tags.html[
+        tags.div(data=tags.directive('name'), render=rend.data)])
+
+class Subclass(Base):
+    def data_name(self, ctx, data):
+        return "Your name"
+    
+ +

+ The data directive is resolved by searching for the + IContainer implementation in the context. + rend.Page implements IContainer.get by + performing an attribute lookup on the Page with the prefix 'data_*'. You + can provide your own IContainer implementation if you wish, + and also you should know that IContainer implementations for + list and dict are included in the nevow.accessors + module. +

+ +

+ A common gotcha is that the closest IContainer is used to + resolve data directives. This means that if a list is being used as the + data during the rendering process, data directives below this will be + resolved against the IContainer implementation in + nevow.accessors.ListAccessor. If you are expecting a data + directive to invoke a Page's data_* method but instead get a + KeyError, this is why. +

+ +

Render directives

+ +

+ Render directives are almost exactly the same, except they are resolved + using the closest IRendererFactory implementation in the + context. Render directives can be used to allow subclasses to override + certain render methods, and also can be used to allow Fragments to + locate their own prefixed render methods. +

+ +

Flatteners

+ +

+ TODO This section isn't done yet. +

+ +

+ Nevow's flatteners use a type/function registry to determine how to + render objects which Nevow encounters in the DOM during the rendering + process. "Explicit is better than implicit", so in most cases, + explicitly applying render methods to data will be better than + registering a flattener, but in some cases it can be useful: +

+ +
+class Person(object):
+    def __init__(self, firstName, lastName):
+        self.firstName = firstName
+        self.lastName = lastName
+
+def flattenPerson(person, ctx):
+    return flat.partialflatten(ctx, (person.firstName, " ", person.lastName))
+
+from nevow import flat
+flat.registerFlattener(flattenPerson, Person)
+
+def insertData(ctx, data):
+    return data
+
+class PersonPage(rend.Page):
+    docFactory = loaders.stan(tags.html[insertData])
+    
+ + diff -Naur Nevow-0.10.0/doc/howto/template.tpl Nevow-0.10.0-new/doc/howto/template.tpl --- Nevow-0.10.0/doc/howto/template.tpl 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/template.tpl 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,24 @@ + + + + + + + Nevow: + + + + + +

+
+
+ +
+ +

Index

+ Version: + + + diff -Naur Nevow-0.10.0/doc/howto/traversal.xhtml Nevow-0.10.0-new/doc/howto/traversal.xhtml --- Nevow-0.10.0/doc/howto/traversal.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/traversal.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,448 @@ + + + + + + + Object Traversal + + + +

Object Traversal

+ +

+ Object traversal is the process Nevow uses to determine + what object to use to render HTML for a particular URL. When an HTTP + request comes in to the web server, the object publisher splits the URL + into segments, and repeatedly calls methods which consume path segments + and return objects which represent that path, until all segments have + been consumed. At the core, the Nevow traversal API is very + simple. However, it provides some higher level functionality layered on + top of this to satisfy common use cases. +

+ +

Object Traversal Basics

+ +

+ The root resource is the top-level object in the URL + space; it conceptually represents the URI /. The Nevow + object traversal and object publishing + machinery uses only two methods to locate an object suitable for + publishing and to generate the HTML from it; these methods are described + in the interface nevow.inevow.IResource: +

+ +
+class IResource(Interface):
+    def locateChild(self, ctx, segments):
+        """Locate another object which can be adapted to IResource
+        Return a tuple of resource, path segments
+        """
+
+    def renderHTTP(self, ctx):
+        """Render a request
+        """
+    
+ +

+ renderHTTP can be + as simple as a method which simply returns a string of HTML. Let's + examine what happens when object traversal occurs over a very simple root + resource: +

+ +
+from zope.interface import implements
+
+class SimpleRoot(object):
+    implements(inevow.IResource)
+
+    def locateChild(self, ctx, segments):
+        return self, ()
+
+    def renderHTTP(self, ctx):
+        return "Hello, world!"
+    
+ +

+ This resource, when passed as the root resource to appserver.NevowSite or wsgi.createWSGIApplication, will immediately return + itself, consuming all path segments. This means that for every URI a user + visits on a web server which is serving this root resource, the text + "Hello, world!" will be rendered. Let's examine the value of + segments for various values of URI: +

+ +
    +
  • / - ('',)
  • +
  • /foo/bar - ('foo', 'bar')
  • +
  • + /foo/bar/baz.html - + ('foo', 'bar', 'baz.html') +
  • +
  • + /foo/bar/directory/ - + ('foo', 'bar', 'directory', '') +
  • +
+ +

+ So we see that Nevow does nothing more than split the URI on the string + / and pass these path segments to our application for + consumption. Armed with these two methods alone, we already have enough + information to write applications which service any form of URL + imaginable in any way we wish. However, there are some common URL + handling patterns which Nevow provides higher level support for. +

+ +

locateChild In Depth

+ +

+ One common URL handling pattern involves parents which only know about + their direct children. For example, a ``Directory`` object may only know + about the contents of a single directory, but if it contains other + directories, it does not know about the contents of them. Let's examine a + simple ``Directory`` object which can provide directory listings and + serves up objects for child directories and files: +

+ +
+from zope.interface import implements
+
+class Directory(object):
+    implements(inevow.IResource)
+
+    def __init__(self, directory):
+        self.directory = directory
+
+    def renderHTTP(self, ctx):
+        html = ['<ul>']
+        for child in os.listdir(self.directory):
+            fullpath = os.path.join(self.directory, child)
+            if os.path.isdir(fullpath):
+                child += '/'
+            html.extend(['<li><a href="', child, '">', child, '</a></li>'])
+        html.append('</ul>')
+        return ''.join(html)
+
+    def locateChild(self, ctx, segments):
+        name = segments[0]
+        fullpath = os.path.join(self.directory, name)
+        if not os.path.exists(fullpath):
+            return None, () # 404
+
+        if os.path.isdir(fullpath):
+            return Directory(fullpath), segments[1:]
+        if os.path.isfile(fullpath):
+            return static.File(fullpath), segments[1:]
+    
+ +

+ Because this implementation of locateChild only consumed one + segment and returned the rest of them (segments[1:]), the + object traversal process will continue by calling + locateChild on the returned resource and passing the + partially-consumed segments. In this way, a directory structure of any + depth can be traversed, and directory listings or file contents can be + rendered for any existing directories and files. +

+ +

+ So, let us examine what happens when the URI + "/foo/bar/baz.html" is traversed, where "foo" + and "bar" are directories, and "baz.html" is a + file. +

+ +
    +
  1. + + Directory('/').locateChild(ctx, ('foo', 'bar', 'baz.html')) + + returns + Directory('/foo'), ('bar', 'baz.html') +
  2. +
  3. + + Directory('/foo').locateChild(ctx, ('bar', 'baz.html')) + + returns + Directory('/foo/bar'), ('baz.html, ) +
  4. +
  5. + + Directory('/foo/bar').locateChild(ctx, ('baz.html')) + + returns + File('/foo/bar/baz.html'), () +
  6. +
  7. + No more segments to be consumed; + File('/foo/bar/baz.html').renderHTTP(ctx) is called, and + the result is sent to the browser. +
  8. +
+ + +

childFactory Method

+ +

+ Consuming one URI segment at a time by checking to see if a requested + resource exists and returning a new object is a very common + pattern. Nevow's default implementation of IResource, nevow.rend.Page, contains an implementation of + locateChild which provides more convenient hooks for + implementing object traversal. One of these hooks is + childFactory. Let us imagine for the sake of example that we + wished to render a tree of dictionaries. Our data structure might look + something like this: +

+ +
+tree = dict(
+    one=dict(
+        foo=None,
+        bar=None),
+    two=dict(
+        baz=dict(
+        quux=None)))
+    
+ +

+ Given this data structure, the valid URIs would be: +

+ +
    +
  • /
  • +
  • /one
  • +
  • /one/foo
  • +
  • /one/bar
  • +
  • /two
  • +
  • /two/baz
  • +
  • /two/baz/quux
  • +
+ +

+ Let us construct a rend.Page + subclass which uses the default locateChild implementation + and overrides the childFactory hook instead: +

+ +
+class DictTree(rend.Page):
+    def __init__(self, dataDict):
+        self.dataDict = dataDict
+
+    def renderHTTP(self, ctx):
+        if self.dataDict is None:
+            return "Leaf"
+        html = ['<ul>']
+        for key in self.dataDict.keys():
+            html.extend(['<li><a href="', key, '">', key, '</a></li>'])
+        html.append('</ul>')
+        return ''.join(html)
+
+    def childFactory(self, ctx, name):
+        if name not in self.dataDict:
+            return rend.NotFound # 404
+        return DictTree(self.dataDict[name])
+    
+ +

+ As you can see, the childFactory implementation is + considerably shorter than the equivalent locateChild + implementation would have been. +

+ +

child_* methods and attributes

+ +

+ Often we may wish to have some hardcoded URLs which are not dynamically + generated based on some data structure. For example, we might have an + application which uses an external CSS stylesheet, an external JavaScript + file, and a folder full of images. The rend.Page.locateChild implementation provides a + convenient way for us to express these relationships by using + child-prefixed methods: +

+ +
+class Linker(rend.Page):
+    def renderHTTP(self, ctx):
+        return """<html>
+<head>
+    <link href="css" rel="stylesheet" />
+    <script type="text/javascript" src="scripts" />
+  <body>
+    <img src="images/logo.png" />
+  </body>
+</html>"""
+
+    def child_css(self, ctx):
+        return static.File('styles.css')
+
+    def child_scripts(self, ctx):
+        return static.File('scripts.js')
+
+    def child_images(self, ctx):
+        return static.File('images/')
+    
+ +

+ One thing you may have noticed is that all of the examples so far have + returned new object instances whenever they were implementing a traversal + API. However, there is no reason these instances cannot be shared. One + could for example return a global resource instance, an instance which + was previously inserted in a dict, or lazily create and cache dynamic + resource instances on the fly. The rend.Page.locateChild + implementation also provides a convenient way to express that one global + resource instance should always be used for a particular URL, the + child-prefixed attribute: +

+ +
+class FasterLinker(Linker):
+    child_css = static.File('styles.css')
+    child_scripts = static.File('scripts.js')
+    child_images = static.File('images/')
+    
+ +

Dots in child names

+ +

+ When a URL contains dots, which is quite common in normal URLs, it is + simple enough to handle these URL segments in locateChild or + childFactory -- one of the passed segments will simply be a + string containing a dot. However, it is not immediately obvious how one + would express a URL segment with a dot in it when using child-prefixed + methods. The solution is really quite simple: +

+ +
+class DotChildren(rend.Page):
+    def renderHTTP(self, ctx):
+        return """
+        <html>
+          <head>
+            <script type="text/javascript" src="scripts.js" />
+          </head>
+        </html>"""
+
+setattr(DotChildren, 'child_scripts.js', static.File('scripts.js'))
+    
+ +

+ The same technique could be used to install a child method with a dot in + the name. +

+ + +

children dictionary

+ +

+ The final hook supported by the default implementation of + locateChild is the rend.Page.children + dictionary: +

+ +
+class Main(rend.Page):
+    children = {
+        'people': People(),
+        'jobs': Jobs(),
+        'events': Events()}
+
+    def renderHTTP(self, ctx):
+        return """
+        <html>
+          <head>
+            <title>Our Site</title>
+          </head>
+          <body>
+            <p>bla bla bla</p>
+          </body>
+        </html>"""
+    
+ +

+ Hooks are checked in the following order: +

+ +
    +
  1. self.children
  2. +
  3. self.child_*
  4. +
  5. self.childFactory
  6. +
+ +

The default trailing slash handler

+ +

+ When a URI which is being handled ends in a slash, such as when the + / URI is being rendered or when a directory-like URI is + being rendered, the string '' appears in the path segments + which will be traversed. Again, handling this case is trivial inside + either locateChild or childFactory, but it may + not be immediately obvious what child-prefixed method or attribute will + be looked up. The method or attribute name which will be used is simply + child with a single trailing underscore. +

+ +

+ The rend.Page class provides an implementation of this + method which can work in two different ways. If the attribute + addSlash is True, the default trailing slash + handler will return self. In the case when + addSlash is True, the default + rend.Page.renderHTTP implementation will simply perform a + redirect which adds the missing slash to the URL. +

+ +

+ The default trailing slash handler also returns self if + addSlash is False, but emits a warning as it + does so. This warning may become an exception at some point in the + future. +

+ +

ICurrentSegments and IRemainingSegments

+ +

+ During the object traversal process, it may be useful to discover which + segments have already been handled and which segments are remaining to be + handled. This information may be obtained from the context + object which is passed to all the traversal APIs. The interfaces nevow.inevow.ICurrentSegments and nevow.inevow.IRemainingSegments are used to retrieve + this information. To retrieve a tuple of segments which have previously + been consumed during object traversal, use this syntax: +

+ +
+segs = ICurrentSegments(ctx)
+    
+ +

+ The same is true of IRemainingSegments. + IRemainingSegments is the same value which is passed as + segments to locateChild, but may also be useful + in the implementations of childFactory or a child-prefixed + method, where this information would not otherwise be available. +

+ +

Conclusion

+ +

+ Nevow makes it easy to handle complex URL hierarchies. The most basic + object traversal interface, nevow.inevow.IResource.locateChild, provides powerful + and flexible control over the entire object traversal process. Nevow's + canonical IResource implementation, rend.Page, + also includes the convenience hooks childFactory along with + child-prefixed method and attribute semantics to simplify common use + cases. +

+ + diff -Naur Nevow-0.10.0/doc/howto/xmltemplates.xhtml Nevow-0.10.0-new/doc/howto/xmltemplates.xhtml --- Nevow-0.10.0/doc/howto/xmltemplates.xhtml 1970-01-01 01:00:00.000000000 +0100 +++ Nevow-0.10.0-new/doc/howto/xmltemplates.xhtml 2010-03-02 20:42:55.000000000 +0100 @@ -0,0 +1,407 @@ + + + + + + + XML Templates + + + +

Nevow XML Templates

+ +

+ Stan syntax is cool, but eventually you are going to want to integrate + your Python code with a template designed by an HTML monkey. Nevow + accomplishes this by providing an xmlfile loader which uses the built-in + Python SAX libraries to generate a tree of stan behind the scenes. The + general rule is anything that is possible in stan should be possible in a + pure XML template; of course, the XML syntax is generally going to be + much more verbose. +

+ +

loaders.xmlfile

+ +

+ Wherever you have seen a loaders.stan being created in any of the example + code, a loaders.xmlfile can be + substituted instead. At the most basic, xmlfile merely + requires the name of an xml template: +

+ +
+class HelloXML(rend.Page):
+    docFactory = loaders.xmlfile('hello.xml')
+    
+ +

+ Placing the following xml in the hello.xml file will cause + HelloXML to display a static page when it is rendered: +

+ +
<html>Hello, world!</html>
+ +

+ The following additional keyword arguments may be given to + xmlfile to configure it: +

+ +
    +
  • + templateDirectory: + The path to the directory which contains the template file. Defaults to ''. +
  • +
  • + ignoreDocType: + If True, discard any DOCTYPE declaration when building the DOM from + this template. When false, preserve the DOCTYPE, causing it to show up + in the final output. Useful for when you are inserting an XML fragment + into a larger page and do not wish to generate invalid XML as + output. Defaults to False. +
  • +
  • + ignoreComment: + If True, discard XML comments, causing them to disappear from the + output. If False, preserve comments and render them in the final output + unchanged. Defaults to False. +
  • +
  • + pattern: + If present, the given pattern name will be looked up and used as the + root of the template. If not present, the entire document will be used + as the template. Useful for embedding fragments of an XML document in a + larger page. Defaults to None. +
  • +
+ +

Nevow's xmlns declaration

+ +

+ In order for Nevow to notice and process any XML directives in the + template file, you must declare the Nevow xmlns at the top of your XML + document. Nevow's xmlns is: +

+ +
http://nevow.com/ns/nevow/0.1
+ +

+ The syntax for declaring that your xml document uses this namespace is: +

+ +
<html xmlns:nevow="http://nevow.com/ns/nevow/0.1"></html>
+ +

+ You may replace the text "nevow" in the above example with any name you + choose. For example, many people use "n" because it is shorter to + type. If you do so, be sure to replace all occurrences of the nevow + namespace in the examples with the namespace name you choose. +

+ +

Nevow's Tag Attribute Language

+ +

+ The markup you will add to your XHTML file in order to invoke Nevow code + consists mostly of namespaced tag attributes. This approach was + influenced heavily by the Zope Page Templates (ZPT) Tag Attribute + Language (TAL). However, I felt that TAL did not go far enough in + removing control flow and branching possibilities from the XML + template. Nevow's main philosophy is that it should be as easy as + possible to move from the XML document into Python code, and that the + Python code should have ultimate control over manipulating the structure + of the XML template. +

+ +

+ The key is that it is easy to expose Python methods that you write to + your XML template, and it is easy for the XML templates to mark nodes + which it wishes the Python method to manipulate. In this way, if either + the Python implementation changes or the location or content of the + marked nodes change in the XML template, the other side will be isolated + from these changes. +

+ +

+ Nevow's XML templating has two attributes which invoke Python code: +

+ +
    +
  • + nevow:render -- + Invokes a Python method and replaces the template node with the result +
  • +
  • + nevow:data -- + Invokes a Python method and sets the data special for the node to the + result +
  • +
+ +

+ It has one attribute which marks nodes as manipulatable by Python code: +

+ +
    +
  • + nevow:pattern -- + Gives a node a name so that Python code may clone and mutate copies of + this node +
  • +
+ +

+ It also has two namespaced tags: +

+ +
    +
  • + nevow:slot -- + Works in the same way as the slot attribute +
  • +
  • + nevow:attr -- + Indicates that an attribute of the parent tag should be manipulated by + Python code in some way +
  • +
+ +

nevow:render

+ +

+ When the nevow:render attribute is encountered, the xmlfile + loader sets the render special to a directive constructed with the + attribute value. When the template is rendered, this means that the + appropriate render_* method will be looked up on the + IRendererFactory (generally the Page instance): +

+ +
<html><div nevow:render="foo" /></html>
+ +

+ With the render_foo method:: +

+ +
+def render_foo(self, ctx, data):
+    return "Hello"
+    
+ +

+ Will result in the document: +

+ +
<html>Hello</html>
+ +

+ Note that the return value of the render method replaces the template + node in the DOM, so if you want the template node to remain, you should + use ctx.tag. +

+ +

Built-in renderers

+ +

+ Nevow comes with various built in renderers on the Page class. +

+ +
    +
  • + data: + Renders the current data as-is inside the current node. +
  • +
  • + string: + Renders the current data as a string inside the current node. +
  • +
  • + sequence: + Iterates the current data, copying the "item" pattern for each + item. Sets the the data special of the new node to the item, and + inserts the result in the current node. See the nevow.rend.sequence + docstring for information about other used patterns, including + "header", "divider", "footer" and "empty". +
  • +
  • + mapping: + Calls .items() on the current data, and calls ctx.fillSlots(key, value) + for every key, value pair in the result. Returns the template tag. +
  • +
  • + xml: + Inserts the current data into the template after wrapping it in an xml + instance. Not very useful in practice. +
  • +
+ +

nevow:data

+ +

+ When the nevow:data attribute is encountered, the xmlfile + loader sets the data special of the current node to a directive + constructed with the attribute value. When the template is rendered, + this means that the appropriate data_* method will be looked up on the + current IContainer (generally the Page instance). The data_* + method will be called, and the result will be set as the data special of + the current Tag: +

+ +
<html><div nevow:data="name" nevow:render="data" /></html>
+ +

+ With the data_name method: +

+ +
+def data_name(self, ctx, data):
+    return "Hello!"
+    
+ +

+ Will result in the document: +

+ +
<html><div>Hello!</div></html>
+ +

+ Note that with a data attribute on a node but no renderer, the result of + the data method will be set as the data special for that tag, and child + render methods will be passed this data. +

+ +

nevow:pattern

+ +

+ When the nevow:pattern attribute is encountered, the xmlfile + loader sets the pattern special of the current node to the attribute + value as a string. Renderers which are above this node may then make + copies of it using the nevow.inevow.IQ of the + current context. With the template: +

+ +
<html nevow:render="stuff"><div nevow:pattern="somePattern" nevow:render="data" /></html>
+ +

+ And the renderer: +

+ +
+def render_stuff(self, ctx, data):
+    pat = inevow.IQ(ctx).patternGenerator('somePattern')
+    return [pat(data=1), pat(data=2)]
+    
+ +

+ Will result in the document: +

+ +
<html><div>1</div><div>2</div></html>
+ +

nevow:slot

+ +

+ When the nevow:slot tag is encountered, the xmlfile loader + constructs a nevow.stan.slot instance, passing + the name attribute value as the slot name. The children of the slot node + are added as children of the new slot instance. This is useful if you + wish to put patterns inside the slot. With the template: +

+ +
<html nevow:render="stuff"><nevow:slot name="slotName" /></html>
+ +

+ And the render method: +

+ +
+def render_stuff(self, ctx, data):
+    ctx.fillSlots('slotName', "Hello.")
+    return ctx.tag
+    
+ +

+ This document will be produced: +

+ +
<html>Hello.</html>
+ +

nevow:attr

+ +

+ When the nevow:attr tag is encountered, the contents of the + nevow:attr node will be assigned to the attribute of the parent tag with + the name of the value of the name attribute. Perhaps an example will be a + little clearer: +

+ +
<html><a><nevow:attr name="href">HELLO!</nevow:attr>Goodbye</a></html>
+ +

+ This document will be produced: +

+ +
<html><a href="HELLO!">Goodbye</a></html>
+ +

+ While this syntax is somewhat awkward, every other type of nevow tag and + attribute may be used inside the nevow:attr node. This makes + setting attributes of tags uniform with every other method of + manipulating the XML template. +

+ +

nevow:invisible

+ +

+ Sometimes you need to group some elements, because you need to use a + renderer for a group of children. +

+ +

+ However, it may not be desirable to give these elements a parent/child + relationship in your XML structure. For these cases, use + nevow:invisible. +

+ +

+ As suggested by the name, a nevow:invisible tag is removed + in the rendered XML. Here is an example: +

+ +
<html><nevow:invisible nevow:data="name" nevow:render="data" /></html>
+ +

+ With the data_name method: +

+ +
+def data_name(self, ctx, data):
+    return "Hello!"
+    
+ +

+ Will result in the document: +

+ +
<html>Hello!</html>
+ +

xmlstr, htmlfile, and htmlstr

+ +

+ xmlstr is a loader which is identical to xmlfile except it takes a string + of XML directly. +

+ +

+ htmlfile and htmlstr should generally be avoided. They are similar to + xmlfile and xmlstr, except they use twisted.web.microdom in + beExtremelyLenient mode to attempt to parse badly-formed HTML (non-XHTML) + templates. See the nevow.loaders docstrings for more information. +

+ +

Conclusions

+ +

+ Nevow's xmlfile tag attribute language allows you to integrate + externally-designed XHTML templates into the Nevow rendering process. +

+ + debian/patches/2662-deprecation-warnings.patch0000644000000000000000000001355512235042114016361 0ustar Author: Jean-Paul Calderone Forwarded: not-needed Description: Change several uses of deprecated APIs in Nevow to more modern equivalents, eliminating the annoying deprecation warnings when loading nevow. --- Nevow/formless/annotate.py 2008-03-24 16:27:18 +0000 +++ Nevow/formless/annotate.py 2010-02-06 04:33:05 +0000 @@ -727,7 +727,7 @@ complexType = property(lambda self: _typedInterfaceMetadata.get((self, 'complexType'), True)) def __new__(cls, name, bases, dct): - rv = cls = InterfaceClass.__new__(cls, name, bases, dct) + rv = cls = InterfaceClass.__new__(cls) _typedInterfaceMetadata[cls, '__id__'] = nextId() _typedInterfaceMetadata[cls, '__methods__'] = methods = [] _typedInterfaceMetadata[cls, '__properties__'] = properties = [] === modified file 'Nevow/nevow/guard.py' --- Nevow/nevow/guard.py 2009-07-06 12:06:26 +0000 +++ Nevow/nevow/guard.py 2010-02-06 04:33:05 +0000 @@ -12,7 +12,10 @@ import random import time -import md5 +try: + from hashlib import md5 +except ImportError: + from md5 import md5 import StringIO from zope.interface import implements @@ -34,7 +37,7 @@ def _sessionCookie(): - return md5.new("%s_%s" % (str(random.random()) , str(time.time()))).hexdigest() + return md5("%s_%s" % (str(random.random()) , str(time.time()))).hexdigest() class GuardSession(components.Componentized): === modified file 'Nevow/nevow/test/test_testutil.py' --- Nevow/nevow/test/test_testutil.py 2008-07-15 16:48:30 +0000 +++ Nevow/nevow/test/test_testutil.py 2010-02-06 04:33:05 +0000 @@ -3,6 +3,8 @@ applications. """ +import sys + from unittest import TestResult from twisted.python.filepath import FilePath @@ -176,6 +178,9 @@ self.case.createSource = lambda testMethod: "throw new TypeError();" self.case.run(result) self.assertEqual(len(result.errors), 1) + self.assertTrue( + result.errors[0][1].startswith( + 'Exception: JavaScript interpreter had error exit: ')) def test_signalledExit(self): @@ -183,13 +188,20 @@ An error should be reported if the JavaScript interpreter exits because it received a signal. """ + segfault = FilePath(__file__).sibling('segfault.py') + def stubFinder(): - return FilePath(__file__).sibling('segfault.py').path + return sys.executable + def stubScript(testModule): + return segfault.path self.case.findJavascriptInterpreter = stubFinder - self.case.createSource = lambda testMethod: "" + self.case.makeScript = stubScript result = TestResult() self.case.run(result) self.assertEqual(len(result.errors), 1) + self.assertEquals( + result.errors[0][1], + 'Exception: JavaScript interpreter exited due to signal 11\n') def test_missingJavaScriptClass(self): === modified file 'Nevow/nevow/testutil.py' --- Nevow/nevow/testutil.py 2009-07-04 00:32:14 +0000 +++ Nevow/nevow/testutil.py 2010-02-06 04:33:05 +0000 @@ -1,12 +1,10 @@ -# Copyright (c) 2004 Divmod. +# -*- test-case-name: nevow.test.test_testutil -*- +# Copyright (c) 2004-2010 Divmod. # See LICENSE for details. import os, sys, signal -try: - from popen2 import Popen3 -except ImportError: - Popen3 = None +from subprocess import PIPE, Popen from zope.interface import implements @@ -15,6 +13,7 @@ except ImportError: subunit = None +from twisted.python.log import msg from twisted.trial.unittest import TestCase as TrialTestCase from twisted.python.components import Componentized from twisted.internet import defer @@ -407,8 +406,6 @@ raise NotSupported("Could not find JavaScript interpreter") if subunit is None: raise NotSupported("Could not import 'subunit'") - if Popen3 is None: - raise NotSupported("Could not import 'popen2.Popen3'") for name in ['WEXITSTATUS', 'WIFSIGNALED' ,'WTERMSIG']: if getattr(os, name, None) is None: raise NotSupported("os.%s unavailable" % (name,)) @@ -497,28 +494,28 @@ # However, *run cannot return a Deferred profanity profanity profanity # profanity*, so instead it is *profanity* this: def run(): - child = Popen3([js, script]) + argv = [js, script] + msg("Running JavaScript interpreter, argv = %r" % (argv,)) + child = Popen(argv, stdout=PIPE) while True: - bytes = child.fromchild.read(4096) + bytes = child.stdout.read(4096) if bytes: protocol.dataReceived(bytes) else: break - exitCode = child.wait() - if os.WIFSIGNALED(exitCode): - result.addError( - self, - (Exception, - "JavaScript interpreter exited due to signal %d" % (os.WTERMSIG(exitCode),), - None)) - else: - exitStatus = os.WEXITSTATUS(exitCode) - if exitStatus: - result.addError( - self, - (Exception, - "JavaScript interpreter had error exit: %d" % (exitStatus,), - None)) + returnCode = child.wait() + if returnCode < 0: + result.addError( + self, + (Exception, + "JavaScript interpreter exited due to signal %d" % (-returnCode,), + None)) + elif returnCode: + result.addError( + self, + (Exception, + "JavaScript interpreter had error exit: %d" % (returnCode,), + None)) self._runWithSigchild(run) debian/copyright0000644000000000000000000000350711024547746011140 0ustar This package was downloaded from http://divmod.org/trac/wiki/DivmodNevow Files: * Copyright: © 2004 Donovan Preston Matt Goodall James Y. Knight Glyph Lefkowitz JP Calderone Allen Short Alex Levy Justin Johnson Christopher Armstrong Jonathan Simms Phil Frost Tommi Virtanen Michal Pasternak Valentino Volonghi License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Files: examples/blogengine/smtpserver.py Copyright: © 2001-2004 Twisted Matrix Laboratories License: MIT Files: nevow/js/Divmod/Base.js Copyright: © 2005 Bob Ippolito License: MIT Files: debian/* Copyright: © 2004 Tommi Virtanen © 2007-2008 Vincent Bernat License: GPL-2+ On Debian systems, the complete text of the GNU General Public License can be found in the file `/usr/share/common-licenses/GPL'. debian/python-nevow.doc-base0000644000000000000000000000065411343273054013251 0ustar Document: nevow Title: Nevow howto and tutorial. Abstract: This document is an introduction to Nevow, a next-generation web application templating system, based on the ideas developed in the Twisted Woven package. The documentation also contains a tutorial for a simple chat system. Section: Programming/Python Format: html Index: /usr/share/doc/python-nevow/howto/index.html Files: /usr/share/doc/python-nevow/howto/*.html debian/rules0000755000000000000000000000350611535077546010267 0ustar #!/usr/bin/make -f # -*- mode: makefile; coding: utf-8 -*- # Copyright © 2002,2003 Colin Walters include /usr/share/cdbs/1/rules/debhelper.mk include /usr/share/cdbs/1/class/python-distutils.mk DEB_INSTALL_EXAMPLES_python-nevow := examples/* DEB_DH_ALWAYS_EXCLUDE := .svn DEB_COMPRESS_EXCLUDE := .py .tac words binary-post-install/python-nevow:: # Clean up stuff find debian/$(cdbs_curpkg) -type d -name .svn -print0 | xargs -0 rm -rf find debian/$(cdbs_curpkg)/usr/share -type f -print0 | xargs -0 chmod -x TOPMODULES:=nevow formless install/python-nevow:: # CDBS and setuptools seems not to be friends with Python 2.6. Move plugins by hand. [ ! -d debian/$(cdbs_curpkg)/usr/twisted ] || \ mv debian/$(cdbs_curpkg)/usr/twisted \ debian/$(cdbs_curpkg)/usr/lib/$(shell pyversions -d)/site-packages # Remove nevow.canvas (see bug #591198) rm debian/$(cdbs_curpkg)/usr/lib/$(shell pyversions -d)/site-packages/nevow/Canvas.swf ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) # Run tests ln -s ../../../doc \ debian/$(cdbs_curpkg)/usr/lib/$(shell pyversions -d)/site-packages/doc $(call cdbs_python_binary,$(shell pyversions -d)) \ debian/runtrial.py debian/$(cdbs_curpkg) $(TOPMODULES) rm debian/$(cdbs_curpkg)/usr/lib/$(shell pyversions -d)/site-packages/doc find debian/$(cdbs_curpkg)/usr -name dropin.cache -print0 | xargs -0 rm endif # Generate documentation cd debian/$(cdbs_curpkg)/usr/doc/howto ; lore *.xhtml */*.xhtml */*/*.xhtml find debian/$(cdbs_curpkg)/usr/doc/howto -name '*.xhtml' -print0 | xargs -0 rm mkdir -p debian/$(cdbs_curpkg)/usr/share/doc/$(cdbs_curpkg) mv debian/$(cdbs_curpkg)/usr/doc/howto debian/$(cdbs_curpkg)/usr/share/doc/$(cdbs_curpkg) rm -rf debian/$(cdbs_curpkg)/usr/doc # Remove nit rm debian/$(cdbs_curpkg)/usr/bin/nit clean:: rm -rf _trial_temp rm -f setupcommon.pyc debian/python-nevow.manpages0000644000000000000000000000003310737511115013355 0ustar doc/man/nevow-xmlgettext.1 debian/README.Debian0000644000000000000000000000035610737511115011234 0ustar To run examples, use the following command line: twistd -d ~ --pidfile=$HOME/nevow.pid -noy examples.tac This will create a `knowledge' file in your home directory. -- Vincent Bernat , Mon, 3 Sep 2007 21:58:55 +0200 debian/postrm0000644000000000000000000000051411425355140010435 0ustar #! /bin/sh set -e case "$1" in remove|purge) find /usr/lib/python[23].*/*-packages/nevow/plugins \ -name dropin.cache 2>/dev/null | xargs -r rm -f for d in /usr/lib/python[23].*/*-packages/nevow/plugins; do rmdir $d 2>/dev/null || true done ;; esac #DEBHELPER# exit 0 debian/python2.4-nevow.manpages0000644000000000000000000000003310257775022013607 0ustar doc/man/nevow-xmlgettext.1 debian/triggers0000644000000000000000000000007411425355474010752 0ustar interest nevow-plugins-cache activate twisted-plugins-cache debian/compat0000644000000000000000000000000210737511115010365 0ustar 5 debian/postinst0000644000000000000000000000110211425355462010775 0ustar #! /bin/sh set -e #DEBHELPER# rebuild_cache() { # remove all cache files, then rebuild for the installed python versions rm -f /usr/lib/python[23].?/*-packages/nevow/plugins/dropin.cache for p in $(pyversions -i); do $p -c 'from twisted.plugin import IPlugin, getPlugins; import nevow.plugins; list(getPlugins(IPlugin)); list(getPlugins(IPlugin, nevow.plugins))' >/dev/null 2>&1 || true done } case "$1" in triggered) if [ "$2" = "nevow-plugins-cache" ]; then rebuild_cache fi ;; configure) rebuild_cache ;; esac exit 0 debian/runtrial.py0000644000000000000000000000062011343267524011405 0ustar import sys from os.path import join from distutils.sysconfig import get_python_lib from site import addsitedir root = sys.argv.pop(1) site_packages = get_python_lib() _path = sys.path[:] sys.path[:] = [] addsitedir(join(root, site_packages[1:])) addsitedir(join(root, site_packages[1:].replace("dist-packages", "site-packages"))) sys.path.extend(_path) from twisted.scripts.trial import run run() debian/watch0000644000000000000000000000012510737511115010216 0ustar version=3 http://divmod.org/trac/wiki/DivmodNevow .*/Nevow-(.*)\.tar\.gz\?format=raw debian/changelog0000644000000000000000000002120212235042555011040 0ustar nevow (0.10.0-5) unstable; urgency=low [ Jakub Wilk ] * Use canonical URIs for Vcs-* fields. * Drop obsolete Breaks/Replaces with python2.3-nevow and python2.4-nevow. [ Vincent Bernat ] * Add patches from trunk to make Nevow work with more recent versions of Twisted. Closes: #713079. * Bump Standards-Version to 3.9.5. -- Vincent Bernat Sat, 02 Nov 2013 01:21:01 +0100 nevow (0.10.0-4) unstable; urgency=low * Fix FTBFS because of a failed test. Closes: #625151. * Bump Standards-Version to 3.9.2. -- Vincent Bernat Wed, 18 May 2011 13:53:11 +0200 nevow (0.10.0-3) unstable; urgency=low * Switch from python-central to dh_python2. Closes: #616914. -- Vincent Bernat Mon, 07 Mar 2011 07:45:33 +0100 nevow (0.10.0-2) unstable; urgency=low * Don't ship Canvas.swf since we don't know how to compile it. nevow.canvas becomes non functional. It is not maintained upstream and could be removed in the future. Closes: #591198. * Bump Standards-Version to 3.9.1. * Fix postrm to purge dropin.cache in nevow plugin directories. Also use twisted-plugins-cache trigger. Provide nevow-plugins-cache trigger for the same purpose. -- Vincent Bernat Sun, 01 Aug 2010 22:09:57 +0200 nevow (0.10.0-1) unstable; urgency=low * New upstream release. * Bump Standards-Version to 3.8.4. * Switch to 3.0 (quilt) format. * Remove dependency on python-all-dev and depends on python instead. * Add a gigantic patch to add missing doc (svn-buildpackage does not support multiple orig tarballs yet). Missing doc is grabbed from SVN (everything matching **/*.{tpl,xhtml}). * In the above patch, alter glossary.xhtml to use H2 tag instead of H3. This closes: #564375. * Fix an FTBFS with Python 2.6 because of the way unittests are run. Also fix a bad interaction between CDBS, setuptools and Python 2.6 in debian/rules. * Remove lintian overrides: not needed anymore. -- Vincent Bernat Tue, 02 Mar 2010 20:37:21 +0100 nevow (0.9.33-5) unstable; urgency=low * Also regenerate dropin.cache for nevow specific plugins on postinst. -- Vincent Bernat Fri, 24 Apr 2009 22:24:05 +0200 nevow (0.9.33-4) unstable; urgency=low * Don't ship dropin.cache. Closes: #525034. -- Vincent Bernat Tue, 21 Apr 2009 21:14:21 +0200 nevow (0.9.33-3) unstable; urgency=low * Regenerate cache after python-central pkginstall has been invoked. Closes: #521663. -- Vincent Bernat Tue, 31 Mar 2009 07:38:38 +0200 nevow (0.9.33-2) unstable; urgency=low * python-central does not like file that are moving out; move them before invoking it. -- Vincent Bernat Fri, 20 Mar 2009 23:21:41 +0100 nevow (0.9.33-1) unstable; urgency=low * New upstream release. * Change the way we run tests to use an helper that will ensure that we use nevow as it will be installed. * Compile documentation with lore. -- Vincent Bernat Fri, 20 Mar 2009 21:52:53 +0100 nevow (0.9.32-2) unstable; urgency=low * Don't try to byte-compile modules with python << 2.4. Closes: #519750. * Bump Standard Version To 3.8.1. No changes. -- Vincent Bernat Sat, 14 Mar 2009 22:24:59 +0100 nevow (0.9.32-1) unstable; urgency=low [ Vincent Bernat ] * New upstream version. - Remove manual page fix for nevow-xmlgettext, applied upstream. - Remove widget plugin fix, applied upstream. - Remove dependency on quilt which is not needed anymore. [ Sandro Tosi ] * debian/control - switch Vcs-Browser field to viewsvn -- Vincent Bernat Sun, 15 Feb 2009 21:02:09 +0100 nevow (0.9.31-4) unstable; urgency=low * Add a patch to make use of Twisted 8.x new plugin API instead of the deprecated one (Closes: #500764). -- Vincent Bernat Wed, 01 Oct 2008 14:52:03 +0200 nevow (0.9.31-3) unstable; urgency=low * Update dropin.cache in postinst and postrm -- Vincent Bernat Mon, 14 Jul 2008 13:04:47 +0200 nevow (0.9.31-2) unstable; urgency=low * Use python-central instead of python-support. This is just for fixing Nevow plugin detection by Twisted. (Closes: #486148) * Removes DM-Upload-Allowed field. * Bump standard version to 3.8.0. * Switch to machine-readble debian/copyright file. * Remove dropin.cache files as well: they can conflict with files from another package. -- Vincent Bernat Fri, 13 Jun 2008 21:34:48 +0200 nevow (0.9.31-1) unstable; urgency=low * New upstream release * Remove now useless patch for setupcommon.py -- Vincent Bernat Sun, 02 Mar 2008 22:33:42 +0100 nevow (0.9.29-1) unstable; urgency=low * New upstream release. * Added Debian Python Modules Team as uploader. Nevow package is now managed in the SVN of DPMT. * Changing section to python. * Changing standard version to 3.7.3. * Set DM-Upload-Allowed to yes to allow me to upload next versions. -- Vincent Bernat Thu, 10 Jan 2008 19:22:50 +0100 nevow (0.9.26-1) unstable; urgency=low * New upstream release -- Vincent Bernat Sat, 10 Nov 2007 10:34:04 +0100 nevow (0.9.18-1) unstable; urgency=low * New upstream release (Closes: #415553). * Adopt the package (Closes: #440619). * Acknowledge NMU (Closes: #376211). * Clean /usr/share/doc/python-nevow, thanks to Jeroen van Wolffelaar (Closes: #436679). * Don't compress examples and explain how to launch them (Closes: #435513). * Add a watch file. * Clean debian/rules and fix trial invocation. -- Vincent Bernat Mon, 03 Sep 2007 21:51:19 +0200 nevow (0.9.0-0.1) unstable; urgency=low * Non-maintainer upload. * New upstream version. (Closes: #376197 - "cannot import nevow", Closes: #376211 - "New upstream version 0.9.0".) I know this is not great procedure for an NMU, but I did run it past Tv, and it's the best way to fix the RC bug. * Bumped up Depends on python-twisted-core and python-twisted-web (necessary with this new nevow). -- paul cannon Fri, 30 Jun 2006 15:10:05 -0600 nevow (0.7.0-1.1) unstable; urgency=low * Non-maintainer upload. * Update nevow to the last python policy (Closes: #373467). -- Pierre Habouzit Thu, 29 Jun 2006 15:10:56 +0200 nevow (0.7.0-1) unstable; urgency=low * New upstream version. * Make python-nevow depend on python2.3-nevow version 0.7.0 or greater. * Switch all twisted dependencies to version 2.2 or greater. * Depend on twisted-core and twisted-web separately. * Build-depend on things needed to run the unit tests. * Switch from Build-Depends-Indep to Build-Depend to ensure everything needed is available during clean. * Switch trial arguments to match what twisted 2.2 expects. (Closes: #358322) * Fix nevow.athena to work with python2.3, the same change is in upstream head already as r3773. -- Tommi Virtanen Sun, 26 Mar 2006 22:25:11 +0300 nevow (0.6.0-1) unstable; urgency=low * New upstream version. (Closes: #336027) * Acknowledge NMU (Closes: #319230), but please be more careful in the future; no NMU patch was sent to BTS * Remove setupcommon.pyc when cleaning, or dpkg-source will see a binary file content change. * Run unit tests when building. * Clean build tree, distutils fails to remove all of it. * Change priority to extra, as twisted is extra and nevow depends on it. -- Tommi Virtanen Sun, 6 Nov 2005 20:26:39 +0200 nevow (0.4.1-1.1) unstable; urgency=low * NMU * Add missing build depends in python2.4-dev (Closes: #319230) * lintian error: fix package description indent for list items. -- Bastian Kleineidam Sat, 13 Aug 2005 18:48:20 +0200 nevow (0.4.1-1) unstable; urgency=low * New upstream version. * Python 2.4 support. * Not using upstream tarball as it is too broken compared to SVN tag; specifically it is missing nevow/Canvas.fla, which is considered source code. -- Tommi Virtanen Mon, 27 Jun 2005 15:35:57 +0200 nevow (0.3.0-1) unstable; urgency=low * New upstream version. -- Tommi Virtanen Thu, 30 Sep 2004 12:12:44 +0300 nevow (0.2.0-2) unstable; urgency=low * Build-depend on both python2.3-dev and python-dev, it seems that is what cdbs wants. (Closes: #257911) -- Tommi Virtanen Tue, 13 Jul 2004 16:39:17 +0300 nevow (0.2.0-1) unstable; urgency=low * Initial Release. -- Tommi Virtanen Tue, 29 Jun 2004 10:26:36 +0300 debian/control0000644000000000000000000000430712235042522010572 0ustar Source: nevow Section: python Priority: optional Maintainer: Vincent Bernat Uploaders: Debian Python Modules Team Standards-Version: 3.9.5 Build-Depends: debhelper (>= 5.0.38), cdbs (>= 0.4.90~), python (>= 2.6.6-3), python-setuptools (>= 0.6b3) Build-Depends-Indep: python-twisted-core (>= 10.1.0-1~), python-twisted-web (>= 10.1.0-1~), python-twisted-lore Vcs-Svn: svn://anonscm.debian.org/python-modules/packages/nevow/trunk/ Vcs-Browser: http://anonscm.debian.org/viewvc/python-modules/packages/nevow/trunk/ Homepage: http://divmod.org/trac/wiki/DivmodNevow X-Python-Version: >= 2.4 Package: python-nevow Architecture: all Depends: ${misc:Depends}, ${python:Depends}, dpkg (>= 1.14.17~), python-twisted-core (>= 10.1.0-1~), python-twisted-web (>= 10.1.0-1~) Provides: ${python:Provides} Description: Web application templating system for Python and Twisted Nevow's 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 debian/pycompat0000644000000000000000000000000210737511115010736 0ustar 2