Commit 5664f5f3 authored by root's avatar root

Add nevow for py3

parent 7fb41afc
...@@ -12,8 +12,9 @@ from txscheduling.interfaces import ISchedule ...@@ -12,8 +12,9 @@ from txscheduling.interfaces import ISchedule
txscheduling.interface.ISchedule interface along with two useful txscheduling.interface.ISchedule interface along with two useful
functions for parsing cron lines. """ functions for parsing cron lines. """
@zope.interface.implementer(ISchedule)
class CronSchedule(object): class CronSchedule(object):
zope.interface.implements(ISchedule) #zope.interface.implements(ISchedule)
_minutes = None _minutes = None
_hours = None _hours = None
......
nevow/_version.py export-subst
*.pyc
*.egg-info
*.cache
*.swp
_trial_temp
build
sudo: false
language: "python"
python:
- "pypy"
- "2.7"
env:
#
# The `MODE` environment variable will control the kind of Nevow installation
# being tested. See below for the precise meaning of each mode.
#
#
# The `TWISTED` environment variable will give a (shell expression which
# evaluates to) a Twisted requirement string which can be used to select a
# version of Twisted to install. This lets the test suite run against
# multiple versions of Twisted. As a cop out, this configuration only
# actually tests the minimum supported version of Twisted, the latest release
# of Twisted, and the master@HEAD version of Twisted.
#
# For expediency, all combinations of `MODE` and `TWISTED` are not tested.
# The "wheel" MODE is (arbitrarily) selected and all values of `TWISTED` are
# exercised in that mode.
#
- MODE=inplace
- MODE=source
- MODE=sdist
- MODE=wheel
- MODE=wheel
TWISTED='echo twisted'
- MODE=wheel
TWISTED='echo -n "twisted=="; python -c "from setup import _MINIMUM_TWISTED_VERSION; print _MINIMUM_TWISTED_VERSION"'
- MODE=wheel
TWISTED='echo git+https://github.com/twisted/twisted.git'
install:
- "python setup.py --version"
- |
# If appropriate, install a specific version of Twisted.
if [ -v TWISTED ]; then
pip install "$(eval ${TWISTED})"
fi
- |
if [ "${MODE}" == "wheel" ]; then
# Build and install a wheel of Nevow.
pip install wheel
python setup.py bdist_wheel
# Install whatever was just built. The filename is tricky to figure
# out because wheels mangle the version in certain ways. Replicate
# some of that mangling here.
pip install dist/Nevow-"$(python setup.py --version | tr - _)"-py2-none-any.whl
# Also install the extra documentation requirements. Unfortunately, it
# seems pip doesn't automatically handle that case when installing from
# a built file.
pip install Sphinx
elif [ "${MODE}" == "sdist" ]; then
# Build and install a normal source distribution
python setup.py sdist
pip install dist/Nevow-"$(python setup.py --version)".tar.gz
# Also install the extra documentation requirements. Unfortunately, it
# seems pip doesn't automatically handle that case when installing from
# a built file.
pip install Sphinx
elif [ "${MODE}" == "inplace" ]; then
# Don't actually install anything. Just update the environment to make
# the source in the git checkout importable. Then run the tests
# directly against that. This is kind of a "control" case. These
# results should be very similar to the results for any of the
# installation cases.
# Note that we also install the documentation requirements.
pip install --editable '.[doc]'
elif [ "${MODE}" == "source" ]; then
# Install directly from the git checkout.
pip install '.'
# Also install the extra documentation requirements. Unfortunately, it
# seems this can't be specified to the install command above.
pip install Sphinx
fi
script:
- "trial formless nevow"
- "make -C doc html"
This diff is collapsed.
Copyright (c) 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
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.
include ChangeLog
include LICENSE
include formless/freeform-default.css
include nevow/Canvas.swf
include nevow/athena_private/*.png
include debian/*
recursive-include twisted *.py
recursive-include nevow *.css *.js
include bin/nevow-xmlgettext
graft doc
recursive-include examples *.css *.gif *.html *.jpg *.js *.py *.png *.tac *.mo *.po *.pot *.xml *.sql *.xul
include examples/i18n/update-l10n
include examples/files/words
include examples/pastebin/data
include examples/pastebin/static/robots.txt
include examples/pastebin/TODO
include extras/xhtml-nevow.rnc
include nevow/Canvas.fla
include nevow/canvas.as
include win32/*
prune */.svn
prune doc/html/*.html
include versioneer.py
include nevow/_version.py
This diff is collapsed.
Divmod Nevow
============
Divmod Nevow is a web application construction kit written in Python. It is
designed to allow the programmer to express as much of the view logic as
desired in Python, and includes a pure Python XML expression syntax named stan
to facilitate this. However it also provides rich support for designer-edited
templates, using a very small XML attribute language to provide bi-directional
template manipulation capability.
Nevow also includes Divmod Athena, a "two way web" or "`COMET`_"
implementation, providing a two-way bridge between Python code on the server
and JavaScript code on the client. Modular portions of a page, known as
"athena fragments" in the server python and "athena widgets" in the client
javascript, can be individually developed and placed on any Nevow-rendered page
with a small template renderer. Athena abstracts the intricacies of HTTP
communication, session security, and browser-specific bugs behind a simple
remote-method-call interface, where individual widgets or fragments can call
remote methods on their client or server peer with one method: "callRemote".
Installation
------------
Before installing Nevow, you should install `Twisted`_, unless you are going to
write very simple CGI applications. Nevow integrates fully with the twisted.web
server providing easy deployment.
Nevow uses the standard distutils method of installation::
python setup.py install
If you do not have Twisted installed, you can run a subset of the tests using
the test.py script. If you have twisted installed, the test.py script will
issue the following trial command::
trial -v nevow.test formless.test
.. _`Twisted`: http://twistedmatrix.com/
Documentation
-------------
More detailed introductory documentation is available in the doc/ directory,
along with the beginnings of a reference manual. A large number of examples are
available in the examples/ directory. These examples require Twisted to run. A
tac file (twisted application configuration) can be started by invoking twistd,
the twisted daemon::
twistd -noy foo.tac
More Information
----------------
Information about `Nevow commits`_ is available on GitHub.
The Nevow git repository can be checked out using::
git clone https://github.com/twisted/nevow.git
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.
.. _`Nevow commits`: https://github.com/twisted/nevow/commits/master
.. _`twisted.web mailing list`: http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
.. _`#twisted.web channel`: irc://irc.freenode.net/#twisted.web
.. _`COMET`: http://alex.dojotoolkit.org/?p=545
from twisted.internet import reactor
from twisted.internet.defer import succeed
from nevow.appserver import NevowSite
from nevow.rend import Page, Fragment
from nevow.page import Element, renderer
from nevow.loaders import stan
from nevow.tags import directive, div, span
class Static:
docFactory = stan("Hello, world. " * 100)
class StaticFragment(Static, Fragment):
pass
class StaticElement(Static, Element):
pass
class Tiny:
docFactory = stan(div(render=directive("foo")))
class TinyFragment(Tiny, Fragment):
def render_foo(self, ctx, data):
return ctx.tag[span["result"]]
class TinyElement(Tiny, Element):
def foo(self, request, tag):
return tag[span["result"]]
renderer(foo)
class Huge:
docFactory = stan(div[[
div(render=directive("foo"))
for x in range(100)]])
class HugeFragment(Huge, Fragment):
def render_foo(self, ctx, data):
return ctx.tag[span["Hello, ", "world", "!"]]
class HugeElement(Huge, Element):
def foo(self, request, tag):
return tag[span["Hello, ", "world", "!"]]
renderer(foo)
class Nested:
docFactory = stan(div(render=directive("foo")))
def __init__(self, count=6):
self.count = count
class NestedFragment(Nested, Fragment):
def render_foo(self, ctx, data):
if self.count:
return span[NestedFragment(self.count - 1)]
return ctx.tag["Hello"]
class NestedElement(Nested, Element):
def foo(self, request, tag):
if self.count:
return span[NestedFragment(self.count - 1)]
return tag["Hello"]
renderer(foo)
class Deferred:
docFactory = stan(div(render=directive('foo')))
class DeferredFragment(Deferred, Fragment):
def render_foo(self, ctx, data):
return ctx.tag[succeed("foo")]
class DeferredElement(Deferred, Element):
def foo(self, request, tag):
return tag[succeed("foo")]
renderer(foo)
class Compare(Page):
def __init__(self, fragment, element):
self.fragment = fragment
self.element = element
def child_fragment(self, ctx):
return Page(docFactory=stan(self.fragment))
def child_element(self, ctx):
return Page(docFactory=stan(self.element))
class Root(Page):
def child_static(self, ctx):
return Compare(StaticFragment(), StaticElement())
def child_tiny(self, ctx):
return Compare(TinyFragment(), TinyElement())
def child_huge(self, ctx):
return Compare(HugeFragment(), HugeElement())
def child_nested(self, ctx):
return Compare(NestedFragment(), NestedElement())
def child_deferred(self, ctx):
return Compare(DeferredFragment(), DeferredElement())
if __name__ == '__main__':
reactor.listenTCP(8080, NevowSite(Root()))
reactor.run()
from __future__ import print_statement, division, absolute_import
from time import time
from twisted.python.usage import Options
from nevow.json import serialize, parse
if __name__ == '__main__':
from json_string_tokenizer import main
raise SystemExit(main())
class StringTokenizer(Options):
optParameters = [
('iterations', 'i', '1000', 'Number of iterations for which to run the benchmark.'),
('scale', 's', '100', 'Factor determining the overall input size.')]
def postOptions(self):
self['iterations'] = int(self['iterations'])
self['scale'] = int(self['scale'])
BASE = 'Hello, world. "Quotes".'
def benchmark(iterations, scale):
"""
Deserialize a string C{iterations} times. Make the string longer based
on C{scale}.
Prints the mean time per parse call.
"""
s = serialize(BASE * scale)
before = time()
for i in range(iterations):
parse(s)
after = time()
print((after - before) / iterations, 'per call')
def main(args=None):
"""
Benchmark nevow.json string parsing, maybe with some parameters.
"""
options = StringTokenizer()
options.parseOptions(args)
benchmark(options['iterations'], options['scale'])
#!/usr/bin/env python
from nevow.scripts.xmlgettext import run
run()
#!/usr/bin/python
from nevow.scripts.nit import run
run()
nevow (0.6.0-1.snapshot) unstable; urgency=low
* SVN snapshot.
-- Tommi Virtanen <tv@debian.org> Sun, 6 Nov 2005 20:45:27 +0200
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 <tv@debian.org> 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 <calvin@debian.org> 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 <tv@debian.org> Mon, 27 Jun 2005 15:35:57 +0200
nevow (0.3.0-1) unstable; urgency=low
* New upstream version.
-- Tommi Virtanen <tv@debian.org> 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 <tv@debian.org> Tue, 13 Jul 2004 16:39:17 +0300
nevow (0.2.0-1) unstable; urgency=low
* Initial Release.
-- Tommi Virtanen <tv@debian.org> Tue, 29 Jun 2004 10:26:36 +0300
Source: nevow
Section: devel
Priority: extra
Maintainer: Tommi Virtanen <tv@debian.org>
Standards-Version: 3.6.2
Build-Depends-Indep: python-dev, python2.3-dev, python2.4-dev, cdbs, debhelper (>= 4.1.68)
Package: python-nevow
Architecture: all
Depends: python (>= 2.3), python (<< 2.4), python2.3-nevow
Description: Web application templating system for Python and Twisted
This is a dummy package that only depends on python2.3-nevow.
Package: python2.3-nevow
Architecture: all
Depends: python2.3, python2.3-twisted
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
Package: python2.4-nevow
Architecture: all
Depends: python2.4, python2.4-twisted
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
This package was debianized by Tommi Virtanen tv@debian.org on
Sun, 28 Mar 2004 16:44:10 +0300.
It was originally downloaded from http://www.divmod.org/Home/Projects/Nevow/
Upstream Author: Donovan Preston et al <dp@divmod.org>
Copyright:
# See the file LICENSE at the top of the source tree.
doc/man/nevow-xmlgettext.1
doc/man/nevow-xmlgettext.1
#!/usr/bin/make -f
# -*- mode: makefile; coding: utf-8 -*-
# Copyright © 2002,2003 Colin Walters <walters@debian.org>
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/python-distutils.mk
DEB_INSTALL_DOCS_python2.3-nevow := doc/*
DEB_INSTALL_EXAMPLES_python2.3-nevow := examples/*
DEB_INSTALL_DOCS_python2.4-nevow := doc/*
DEB_INSTALL_EXAMPLES_python2.4-nevow := examples/*
DEB_DH_ALWAYS_EXCLUDE := .svn
docdir = debian/$(1)/usr/share/doc/$(1)
binary-post-install/%::
grep -v '^# See the file LICENSE' \
'$(call docdir,$*)/copyright' \
>'$(call docdir,$*)/copyright.tmp'
cat LICENSE \
>>'$(call docdir,$*)/copyright.tmp'
mv \
'$(call docdir,$*)/copyright.tmp' \
'$(call docdir,$*)/copyright'
# see http://bugs.debian.org/295906
cdbs_python_ver = $(filter-out -%,$(subst -, -,$(patsubst python%,%,$(cdbs_curpkg))))
$(patsubst %,binary-post-install/%,$(DEB_PYTHON_REAL_LIB_PACKAGES)):: binary-post-install/%:
set -e; for file in debian/$(cdbs_curpkg)/usr/bin/*; do \
sed '1s|.*|#!/usr/bin/python$(cdbs_python_ver)|' $$file >\
"$${file}$(cdbs_python_ver)";\
rm -- "$$file";\
chmod 755 "$${file}$(cdbs_python_ver)";\
mv "debian/$(cdbs_curpkg)/usr/share/man/man1/$$(basename "$$file").1" \
"debian/$(cdbs_curpkg)/usr/share/man/man1/$$(basename "$$file")$(cdbs_python_ver).1";\
done
binary-post-install/python2.3-nevow::
set -e; for file in debian/$(cdbs_curpkg)/usr/bin/*;\
do target="$$(echo "$$file" | sed 's/$(cdbs_python_ver)$$//')";\
ln -s "$$(basename "$$file")" "$$target";\
manname="$$(basename "$$target").1.gz";\
ln -s "$$(basename "$$file").1.gz" \
"debian/$(cdbs_curpkg)/usr/share/man/man1/$$manname";\
done
clean::
rm -f setupcommon.pyc
ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS)))
TRIAL=trial$(cdbs_python_ver)
TOPMODULES:=nevow formless
$(patsubst %,binary-post-install/%,$(DEB_PYTHON_REAL_LIB_PACKAGES)):: binary-post-install/%:
PYTHONPATH='debian/$(cdbs_curpkg)/usr/lib/python$(cdbs_python_ver)/site-packages/' \
'$(TRIAL)' --bwverbose -R $(TOPMODULES)
# Importing the modules generates .pyc files, and dh_python (which
# normally cleans them) has already been run. Remove them manually.
find 'debian/$(cdbs_curpkg)' -name '*.py[co]' -print0 \
| xargs -0 rm -f --
endif
clean::
rm -rf _trial_temp
# distutils is sloppy and only cleans with the default python version,
# leaving all the other stuff still in build
clean::
rm -rf build
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Nevow.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Nevow.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Nevow"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Nevow"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
# -*- coding: utf-8 -*-
#
# Nevow documentation build configuration file, created by
# sphinx-quickstart on Sun Jun 22 13:36:57 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
from nevow import __version__
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Nevow'
copyright = '2014, Twisted developers'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all
# documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Nevowdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'Nevow.tex', 'Nevow Documentation',
'Twisted developers', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'nevow', 'Nevow Documentation',
['Twisted developers'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Nevow', 'Nevow Documentation',
'Twisted developers', 'Nevow', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
Concepts of Athena: AJAX, COMET, and Python
===========================================
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:
.. code-block:: javascript
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:
.. code-block:: python
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:
.. code-block:: python
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``:
.. code-block:: python
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:
.. code-block:: javascript
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.
Setting Up the Tutorial Environment and Running Tutorial Source Code
====================================================================
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 <http://divmod.org/trac/wiki/DivmodCombinator>`__ 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. If you don't have the `twisted
library <http://twistedmatrix.com/trac/>`__, check it out now::
$ svn co svn://svn.twistedmatrix.com/svn/Twisted/trunk Twisted/trunk
3. Then get Combinator and Nevow (and the rest of Divmod). See the
`Combinator
Tutorial <http://divmod.org/trac/wiki/CombinatorTutorial>`__ for more
about these special checkout paths.::
$ svn co http://divmod.org/svn/Divmod/trunk Divmod/trunk
4. 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)
5. Register both the Twisted and Divmod (and thus Nevow+Athena)
codebases with Combinator::
$ chbranch Twisted trunk
$ chbranch Divmod trunk
6. 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.
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. Checkout and install the latest Twisted::
$ svn co svn://svn.twistedmatrix.com/svn/Twisted/trunk Twisted
$ cd Twisted
$ sudo python setup.py install
$ cd ../
3. Checkout and install Nevow::
$ svn co http://divmod.org/svn/Divmod/trunk/Nevow Nevow
$ cd Nevow
$ sudo python setup.py install
$ cd ../
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.
.. toctree::
:maxdepth: 2
intro
concepts
env
part00/index
part01/index
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.
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 <http://divmod.org/trac/wiki/DivmodNevow>`__,
`Django <http://www.djangoproject.com/>`__,
`TurboGears <http://turbogears.org/>`__, or maybe even `Ruby On
Rails <http://rubyonrails.org/>`__. 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 <http://twistedmatrix.com/>`__-based web framework.
- Similar to Twisted's `Perspective
Broker <http://twistedmatrix.com/projects/core/documentation/howto/pb-intro.html>`__,
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.
Toy 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 :doc:`../intro` and the :doc:`../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. We will have a client that:
- communicates with the server;
- updates its DOM tree.
The user experience of this application will be the following:
1. they will type text in an input on a form; and
2. the typed text will be rendered to a different part of the page upon
hitting a submit button.
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. Python code will receive messages from the client;
3. Python code will send messages to the client; and
4. a template file (or ``stan`` code) will be used for presentation.
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:
.. literalinclude:: listings/echothing/template.html
:language: html
:linenos:
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. inherit ``callRemote`` functionality from the base ``Widget`` class;
3. setup convenience attributes;
4. implement the ``doSay()`` method we put in our template above; and
5. implement a method for updating the DOM with data it receives from
the server:
.. literalinclude:: listings/echothing/js/EchoThing.js
:language: javascript
:linenos:
Points to note:
- Those import statements aren't just pretty: they are necessary! In
Athena, you need to treat those like you treat the import statements
in Python.
- The attributes set in the ``__init__()`` method are for convenience
when we reference them in other methods.
- Note the ``callRemote()`` method in ``doSay()``, As mentioned in the
`Concepts <../concepts.html>`__ section, this is how JavaScript is
communicating with our Python server.
- Another thing about ``doSay``: this is the submit handler. As such,
it needs to return false so that the browser is prevented from doing
a normal form submission.
- ``addText()`` is the method that will be updating the browser DOM
once the server sends the data back.
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):
.. literalinclude:: listings/nevow/plugins/echothing_package.py
:language: python
:linenos:
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:
.. literalinclude:: listings/echothing/echobox.py
:language: python
:linenos:
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:
- The ``jsClass`` assignment is what connects this code to your
JavaScript code.
- As discussed in the `Concepts <../concepts.html>`__ section, the
``expose`` decorator is required if our JavaScript is going to be
able to call the ``say()`` method.
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. after typing a message in the input box, the user hits submit;
3. upon hitting submit, the client code ``doSay()`` method is called;
4. ``doSay()`` makes a remote call to the Python server method
``say()``;
5. the Python server receives the data when ``say()`` is called, and
then it passes that data to the client code's ``addText()`` method;
6. 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;
7. when ``addText()`` finishes, the cycle has completed and the browser
now displays the latest data input by the user.
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 <http://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.
from twisted.python.util import sibpath
from nevow.athena import LiveElement, expose
from nevow.loaders import xmlfile
class EchoElement(LiveElement):
docFactory = xmlfile(sibpath(__file__, 'template.html'))
jsClass = 'EchoThing.EchoWidget'
def say(self, message):
self.callRemote('addText', message)
say = expose(say)
// import Nevow.Athena
Nevow.Athena.Widget.subclass(EchoThing, 'EchoWidget').methods(
function __init__(self, node) {
EchoThing.EchoWidget.upcall(self, "__init__", node);
self.echoWidget = self.nodeByAttribute('name', 'echoElement');
self.scrollArea = self.nodeByAttribute('name', 'scrollArea');
self.message = self.nodeByAttribute('name', 'message');
},
function doSay(self) {
self.callRemote("say", self.message.value);
self.message.value = "";
return false;
},
function addText(self, text) {
var newNode = document.createElement('div');
newNode.appendChild(document.createTextNode(text));
self.scrollArea.appendChild(newNode);
document.body.scrollTop = document.body.scrollHeight;
});
<div xmlns:nevow="http://nevow.com/ns/nevow/0.1"
xmlns:athena="http://divmod.org/ns/athena/0.7"
nevow:render="liveElement">
<h2>Echo Element</h2>
<form name="echoElement">
<athena:handler event="onsubmit" handler="doSay" />
<div name="scrollArea">
</div>
<input name="message" /><input type="submit" value="Send" />
</form>
</div>
from twisted.python import util
from nevow import athena
import echothing
chatthingPkg = athena.AutoJSPackage(util.sibpath(echothing.__file__, 'js'))
Simple Chat and Two-Way Communications
======================================
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. 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;
3. they will type text in the input field; and
4. the typed text will appear in the browser of every person who is
logged in.
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. Python code will receive messages from the client;
3. Python code will process these messages;
4. Python code will send messages to the all clients; and
5. a template file (or ``stan`` code) will be used for presentation.
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:
.. literalinclude:: listings/chatthing/template.html
:language: html
:linenos:
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:
.. literalinclude:: listings/chatthing/js/ChatThing.js
:language: javascript
:linenos:
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:
.. literalinclude:: listings/nevow/plugins/chatthing_package.py
:language: python
:linenos:
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.
.. literalinclude:: listings/chatthing/chatterbox.py
:language: python
:linenos:
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 ``ChatterElement``\ es
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. after typing a message in the input box, the user hits submit;
3. JavaScript client code calls to the server with the text the user
submitted;
4. the server gets the message and shares it with all the connected
``ChatterElement``\ s;
5. each ``ChatterElement`` hears this message and passes it back to the
JavaScript client;
6. the client prepends the username to the message and then updates the
display with the complete message.
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 ``ChatterElement``\ es. 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.
from twisted.python.util import sibpath
from nevow.loaders import xmlfile
from nevow.athena import LiveElement, expose
class ChatRoom(object):
def __init__(self):
self.chatters = []
def wall(self, message):
for chatter in self.chatters:
chatter.wall(message)
def tellEverybody(self, who, what):
for chatter in self.chatters:
chatter.hear(who.username, what)
def makeChatter(self):
elem = ChatterElement(self)
self.chatters.append(elem)
return elem
# element to be run with twistd
chat = ChatRoom().makeChatter
class ChatterElement(LiveElement):
docFactory = xmlfile(sibpath(__file__, 'template.html'))
jsClass = 'ChatThing.ChatterWidget'
def __init__(self, room):
self.room = room
def setUsername(self, username):
self.username = username
message = ' * user '+username+' has joined the room'
self.room.wall(message)
setUsername = expose(setUsername)
def say(self, message):
self.room.tellEverybody(self, message)
say = expose(say)
def wall(self, message):
self.callRemote('displayMessage', message)
def hear(self, username, what):
self.callRemote('displayUserMessage', username, what)
// import Nevow.Athena
Nevow.Athena.Widget.subclass(ChatThing, 'ChatterWidget').methods(
function __init__(self, node) {
ChatThing.ChatterWidget.upcall(self, "__init__", node);
self.chooseBox = self.nodeByAttribute('name', 'chooseBox');
self.scrollArea = self.nodeByAttribute('name', 'scrollArea');
self.sendLine = self.nodeByAttribute('name', 'sendLine');
self.usernameField = self.nodeByAttribute('name', 'username');
self.userMessage = self.nodeByAttribute('name', 'userMessage');
self.loggedInAs = self.nodeByAttribute('name', 'loggedInAs');
},
function doSetUsername(self) {
var username = self.usernameField.value;
self.callRemote("setUsername", username).addCallback(
function (result) {
self.chooseBox.style.display = "none";
self.sendLine.style.display = "block";
self.loggedInAs.appendChild(document.createTextNode(username));
self.loggedInAs.style.display = "block";
});
return false;
},
function doSay(self) {
self.callRemote("say", self.userMessage.value);
self.nodeByAttribute('name', 'userMessage').value = "";
return false;
},
function displayMessage(self, message) {
var newNode = document.createElement('div');
newNode.appendChild(document.createTextNode(message));
self.scrollArea.appendChild(newNode);
document.body.scrollTop = document.body.scrollHeight;
},
function displayUserMessage(self, avatarName, text) {
var msg = avatarName+': '+text;
self.displayMessage(msg);
});
<div xmlns:nevow="http://nevow.com/ns/nevow/0.1"
xmlns:athena="http://divmod.org/ns/athena/0.7"
nevow:render="liveElement">
<h2>Chatter Element</h2>
<form name="chatBox">
<athena:handler event="onsubmit" handler="doSay" />
<div name="scrollArea"
style="border: 1px solid gray; padding: 5; margin: 5">
</div>
<div name="sendLine" style="display: none">
<input name="userMessage" /><input type="submit" value="Send" />
</div>
</form>
<form name="chooseBox">
<athena:handler event="onsubmit" handler="doSetUsername" />
Choose your username: <input name="username" />
<input type="submit" name="GO" value="Enter"/>
</form>
<div name="loggedInAs" style="display:none"><span>Logged in as </span></div>
</div>
from twisted.python import util
from nevow import athena
import chatthing
chatthingPkg = athena.AutoJSPackage(util.sibpath(chatthing.__file__, 'js'))
Deployment
==========
Nevow includes two major phases for deciding what HTML to render.
:doc:`traversal` is the procedure by which a URL
is mapped to a Python object which will perform the HTML generation.
:ref:`glossary-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
:ref:`glossary-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:
.. code-block:: python
#!/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 :doc:`traversal` features, and you
have to generate HTTP headers yourself. WSGI is a slightly higher-level
deployment option which does not suffer these problems.
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.athena``. Currently, these modules require
more control over the HTTP socket than CGI 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:
.. code-block:: python
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.
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.athena.
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.
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``:
.. literalinclude:: listings/gettingstarted/helloworld.py
:language: python
:linenos:
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:
.. literalinclude:: listings/gettingstarted/helloworld.tac
:language: python
:linenos:
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.
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.
.. _glossary-page_rendering:
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.
.. _glossary-deployment_environment:
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.
============
Introduction
============
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
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:
.. code-block:: html
<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:
.. code-block:: python
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:
.. code-block:: html
<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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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:
.. code-block:: python
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 <https://divmod.org/trac/wiki/DivmodNevow>`__ 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 <http://twistedmatrix.com/trac/wiki/TwistedWeb>`__ server. Nevow is
an active project, and many new bugfixes and features are committed to
the Nevow Git repository. Information about Nevow commits is available
by subscribing to the `Divmod
commits <http://divmod.net/users/mailman.twistd/listinfo/divmod-commits>`__
mailing list. The Nevow Git repository can be checked out using:
::
git clone git://github.com/twisted/nevow
Discussion of Nevow occurs on the `twisted.web mailing
list <http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web>`__.
The Nevow developers are also often available for real-time help on the
`#twisted.web channel <irc://irc.freenode.net/#twisted.web>`__ on
irc.freenode.net.
from nevow import loaders, rend
class HelloWorld(rend.Page):
addSlash = True
docFactory = loaders.xmlfile('helloworld.html')
from twisted.application import internet
from twisted.application import service
from nevow import appserver
import helloworld
application = service.Application('helloworld')
site = appserver.NevowSite(helloworld.HelloWorld())
webServer = internet.TCPServer(8080, site)
webServer.setServiceParent(application)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Welcome to the Nevow documentation!
===================================
- :doc:`howto/intro`
- :doc:`howto/gettingstarted`
A basic introduction to rendering a web page in Nevow.
- :doc:`howto/traversal`
Getting from an URL to a Python page object you want to render.
- :doc:`howto/publishing`
Exposing Python objects as parts of a web page in Nevow.
- :doc:`howto/xmltemplates`
Using standard XHTML as a template for Nevow.
- :doc:`howto/deployment`
How to get your Nevow application running on different types of servers.
- :doc:`howto/chattutorial/index`
Two-way communication with JavaScript in a web browser.
- :doc:`howto/glossary`
Full documentation
------------------
.. toctree::
:maxdepth: 2
:includehidden:
howto/intro
howto/gettingstarted
howto/traversal
howto/publishing
howto/xmltemplates
howto/deployment
howto/chattutorial/index
howto/glossary
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment