summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Bicking <ian@ianbicking.org>2005-09-27 04:58:55 +0000
committerIan Bicking <ian@ianbicking.org>2005-09-27 04:58:55 +0000
commit9f77cff2379dd6162487fee22cfc794a4617929c (patch)
tree7fc05cccea06f7258fd74bac49b8ea47b3f78789
parent30823489a0317035a3cad425fc3e24af160d917f (diff)
downloadpaste-git-9f77cff2379dd6162487fee22cfc794a4617929c.tar.gz
Refactored and culled documentation
-rw-r--r--docs/Paste.txt232
-rw-r--r--docs/TodoTutorial.txt998
-rw-r--r--docs/blog-tutorial.txt374
-rw-r--r--docs/index.txt146
-rw-r--r--docs/roadmap.txt208
-rw-r--r--docs/what-is-paste.txt177
-rw-r--r--setup.cfg13
7 files changed, 131 insertions, 2017 deletions
diff --git a/docs/Paste.txt b/docs/Paste.txt
deleted file mode 100644
index 3cd24f0..0000000
--- a/docs/Paste.txt
+++ /dev/null
@@ -1,232 +0,0 @@
-Python Paste
-============
-
-This is a WSGI_ version of WebKit. In addition to supporting WSGI, it
-also is a simplification and refactoring of Webware.
-
-.. _WSGI: http://www.python.org/peps/pep-0333.html
-
-License
--------
-
-Paste is distributed under the `Python Software Foundation`__
-license. This is a BSD/MIT-style license.
-
-.. __: http://www.python.org/psf/license.html
-
-Quick Start
------------
-
-First, grab WSGIUtils, at http://www.owlfish.com/software/wsgiutils/,
-or download it directly from
-http://www.owlfish.com/software/wsgiutils/downloads/WSGI%20Utils-0.5.tar.gz
-
-You can use other servers with Paste, but WSGIUtils is pretty easy
-and built on SimpleHTTPServer. Run ``python setup.py install`` to
-install it.
-
-Right now it's best NOT to install Paste with ``setup.py``, but just
-to run it out of the checkout. An easy way to do that::
-
- ./scripts/app-setup create webkit_zpt /path/to/put/files
- cd /path/to/put/files
- /path/to/paste/scripts/server -v
-
-And it will be running a server on http://localhost:8080/
-
-Compatibility
--------------
-
-This system is intended to be largely compatible with current Webware
-applications. This is only an intention; use with real-world
-applications will help us refine this compatibility.
-
-Some parts that aren't being brought over:
-
-* Configuration. Right now configuration is a little inconsistent.
-
-* Contexts. These were half-formed in Webware, and didn't really
- provide much benefit. There are much more general and useful ways
- to deal with URLs.
-
-* URL introspection. I tried to get some of it, but there's way too
- many ways of refactoring the URL in Webware, and I didn't deal with
- all of them. The environment (``request.environ()``) may look
- different than in Webware -- it matches the WSGI expectations. It's
- certainly possible -- and hopefully clearer -- to do introspection
- in Paste, but it might not be backward compatible (but still file
- bugs if you find problems).
-
-Discussion
-----------
-
-Discussion should take place on the Webware mailing list,
-webware-discuss@lists.sf.net and webware-devel@list.sf.net
-
-Configuration
--------------
-
-Configuration is done with normal Python files. Global variables
-become configuration values. So a configuration file might look
-like::
-
- host = 'localhost'
- port = 8080
- publish_dir = '/path/to/dir'
- database_name = 'app_data'
-
-And so on. There's a default configuration file in
-``paste/default_config.conf`` which will also serve as
-documentation. Your configuration overrides those values. The
-extension is arbitrary at this point.
-
-Some important configuration values:
-
-``config_file``:
- Load the given configuration file.
-
-``server``:
- The (string) name of the server you want to use. Right now there
- is ``"wsgiutils"`` and ``"twisted"``.
-
-``publish_dir``:
- The base directory to find Webware servlets. (In the future other
- frameworks will also be supported.)
-
-``debug``:
- If true, then errors will be displayed in the browser.
-
-``reload``:
- If true, then we'll monitor for any changed modules and restart
- the server if those are found.
-
-``verbose``:
- If true, talk a lot.
-
-Use
----
-
-These are some manual ways to set up a WSGI server, but
-``scripts/server`` is the easiest.
-
-This script takes takes options that turn into configuration
-parameters. If there's a ``port`` configuration value you can also
-use the ``--port=X`` command-line option. ``-`` is also replaced with
-``_``.
-
-Another option is ``-f config_file.conf`` which loads the given
-configuration file. This might be the only option you need, if you
-put all the other settings in the configuration file.
-
-Note that ``--verbose``, ``--reload`` and ``--debug`` do not require
-an argument.
-
-Multiple Applications / Manual Setup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Right now integration of multiple applications into a single server is
-kind of an unsolved problem, but manual application setup would be the
-way to go about it...
-
-CGI
-~~~
-
-The ``cgiserver`` script is included. It's not particularly good,
-but it's there for you. To use it, you'll set up your own CGI script,
-like::
-
- #!/usr/bin/env python
- # maybe import sys and modify sys.path
- from paste import cgiserver
- from paste.webkit import wsgiwebkit
- app = wsgiwebkit.webkit('/path/to/app')
- cgiserver.run_with_cgi(app)
-
-Twisted
-~~~~~~~
-
-This package includes ``twisted_wsgi``, which is a WSGI adapter
-written by Peter Hunt. The canonical location for this file is:
-http://st0rm.hopto.org:8080/wsgi/ (note -- Twisted 2.0 will include
-native WSGI support; there's been reports of bugs in the version we
-are currently shipping).
-
-This is closer to the normal Webware environment than CGI. To set up
-this server use::
-
- #!/usr/bin/env python
- # maybe import sys and modify sys.path
- from paste import twisted_wsgi
- from paste.webkit import wsgiwebkit
- app = wsgiwebkit.webkit('/path/to/app')
- twisted_wsgi.serve_application(app, 8080)
-
-Note, the twisted server is dependent on wsgiref which can be found at
-http://cvs.eby-sarna.com/wsgiref
-
-
-Multi-threaded
-~~~~~~~~~~~~~~
-
-WSGIUtils_ includes a SimpleHTTPServer_-based multi-threaded server.
-This is an architecture very similar to Webware's AppServer. Twisted
-may be the better approach, but who knows?
-
-.. _WSGIUtils: http://www.owlfish.com/software/wsgiutils/
-.. _SimpleHTTPServer: http://python.org/doc/current/lib/module-SimpleHTTPServer.html
-
-To set up this server use::
-
- #!/usr/bin/env python
- from wsgiutils import wsgiServer
- from paste.webkit import wsgiwebkit
- app_root='/path/to/app'
- app = wsgiwebkit.webkit(app_root)
- server = wsgiServer.WSGIServer(('127.0.0.1', 8080), {'/': app})
- server.serve_forever()
-
-Interactive
-~~~~~~~~~~~
-
-This allows you to test an application without running any web server.
-While you can use this several ways (e.g., for acceptance tests), this
-is a simple way::
-
- #!/usr/bin/env python
- # maybe import sys and modify sys.path
- from paste import wsgilib
- from paste.webkit import wsgiwebkit
- app = wsgiwebkit.webkit('/path/to/app')
- def run(url):
- print wsgilib.interactive(application, url)
- if __name__ == '__main__':
- import sys
- url = sys.argv[1]
- run(url)
-
-Then you run this script with the URL you want to access; the headers
-and page will be printed out. This can also be used at the
-interactive prompt, e.g., ``python -i interactiveapp.py``, and you can
-use pdb_ or other debugging tools with this.
-
-.. _pdb: http://python.org/doc/current/lib/module-pdb.html
-
-Comparison of architectures
----------------------------
-
-WSGI WebKit's implementation of ``Page`` (and ``HTTPServlet``)
-implements a ``__call__`` method, which is all that is required to
-make a servlet into a WSGI application. The __call__ method creates
-the transaction (``wktransaction.Transaction``), which in turn creates
-the request (``wkrequest.HTTPRequest``) and response
-(``wkresponse.HTTPResponse``). Then there's a bit of back-and-forth
-
-There are also session and application objects, which are largely
-dummy objects that provide some of the interface that was previously
-implemented by those objects. In WSGI WebKit these features
-(forwarding and sessions) are implemented in middleware.
-
-This is in contrast to normal Webware, where the ``AppServer`` starts
-the connection, hands it off to ``Application``, which creates
-``Transaction``, ``HTTPRequest``, and ``HTTPResponse``, then finds the
-servlet and runs it with those objects.
diff --git a/docs/TodoTutorial.txt b/docs/TodoTutorial.txt
deleted file mode 100644
index 8b9a62a..0000000
--- a/docs/TodoTutorial.txt
+++ /dev/null
@@ -1,998 +0,0 @@
-+++++++++++++++++
-To-Do: A Tutorial
-+++++++++++++++++
-
-:author: Ian Bicking <ianb@colorstudy.com>
-:revision: $Rev$
-:date: $LastChangedDate$
-
-.. contents::
-
-.. comment (about this document)
-
- This document is meant to be processed with
- paste/tests/doctest_webapp.py, which assembles the file and
- provides a degree of testing. The pages inlined aren't current
- tested, and so must be inspected by eye after the document is
- assembled.
-
-Introduction and audience
-=========================
-
-This tutorial is intended for people interested in developing
-applications in Paste.
-
-This is a tutorial for building a simple to-do list application using
-`Python Paste <http://pythonpaste.org>`_, SQLObject_, and `Zope Page Templates`_. You can view the
-completed application in the repository at ``examples/todo_sql`` or
-view the repository online at
-http://svn.w4py.org/Paste/trunk/examples/todo_sql/
-
-.. _SQLObject: http://sqlobject.org
-.. _Zope Page Templates: http://www.zope.org/DevHome/Wikis/DevSite/Projects/ZPT/FrontPage
-
-.. include:: include/contact.txt
-
-Setting up the files
-====================
-
-First, get the latest version of Paste with::
-
- svn co http://svn.pythonpaste.org/Paste/trunk Paste
- cd Paste
- python build-pkg.py
-
-That last line downloads a lot of dependencies and installs them
-locally to Paste. You can re-run this command to update these
-libraries as well.
-
-.. note::
-
- We're doing all this in the Python interpreter, even though you'd normally do
- some of this in the shell. This way the authors of this tutorial
- can use something called doctest_, which allows this tutorial to
- be tested Python in an automated way.
-
- .. _doctest: http://python.org/doc/current/lib/module-doctest.html
-
-.. comment (setup doctests)
-
- >>> from paste.tests.doctest_webapp import *
-
-Let's start out quickly. We'll be installing the application in
-``/var/www/example-builds/todo_sql``:
-
-.. comment (setup)
-
- >>> BASE = '/var/www/example-builds/todo_sql'
- >>> import sys
- >>> sys.path.append('/path/to/Paste')
- >>> clear_dir(BASE)
- >>> run("paster create --template=webkit_zpt %s" % BASE)
- >>> os.chdir(BASE)
- >>> ls(recurse=True)
- __init__.py
- server.conf
- sitepage.py
- templates/
- generic_error.pt
- index.pt
- standard_template.pt
- web/
- __init__.py
- index.py
- static/
- stylesheet.css
-
-::
-
- $ export PYTHONPATH=/path/to/Paste:$PYTHONPATH
- $ BASE=/var/www/example-builds/todo_sql
- $ paster create --template=webkit_zpt $BASE
- $ cd $BASE
- $ ls -R
- .:
- __init__.py
- server.conf
- sitepage.py
- templates/
- web/
-
- ./templates:
- generic_error.pt
- index.pt
- standard_template.pt
-
- ./web:
- index.py
- __init__.py
- static/
-
- ./web/static:
- stylesheet.css
-
-I want to give a quick explanation of the file structure and the
-nature of the files.
-
-``__init__.py``:
- This is a special file that Python looks for in a "package". It
- needs to exist for Python to allow you to import modules from the
- directory. This module is also the first thing imported in our
- application, so any setup code can go here.
-
-``server.conf``:
- This contains the main configuration of our application. This is
- a python-format file, and all the variables assigned in it are
- configuration keys.
-
-``sitepage.py``:
- This module contains an abstract class that all our servlets
- subclass from. Each servlet is a class, and a page in our web
- application; by making them all subclass from a single class
- (``SitePage``) we can provide functionality that is global to our
- application.
-
-``templates/``:
- This contains all the "templates" -- there is a template for every
- page in our application.
-
-``templates/standard_template.pt``:
- This defines the look of our application -- changes we make here
- affect all pages in the application.
-
-``templates/index.pt``:
- This is a template for a single servlet (the ``index`` servlet, in
- ``web/index.py``).
-
-``templates/generic_error.pt``:
- This template is used when we just want to output a simple
- message to the user, wrapped in the site look.
-
-``web/``:
- This directory contains your servlets. Actually, any file in this
- directory will be served up -- ``.py`` files are expected to
- contain servlets, and most other files are served up as
- themselves.
-
-``web/__init__.py``:
- Unlike the other ``.py`` files, this one is special, and can
- contain "hooks" called during the URL parsing.
-
-``web/index.py``:
- This is a simple example servlet. Anything named ``index`` is
- also used as the default page (like ``index.html``). Paste
- mostly ignores extensions when finding pages, so ``/index`` can
- refer to ``index.py``, ``index.html``, or any other page named
- ``index`` regardless of extension.
-
-``web/static/``:
- This contains files that don't have any dynamic content, like
- images and Javascript. Based on deployment, these files could be
- served up by Apache or another web server without Paste being
- involved at all (with some CPU savings), so we keep them
- separated.
-
-``web/static/stylesheet.css``:
- A simple stylesheet for your application.
-
-
-Running the application
------------------------
-
-It's just the barest example application, but we can still run it and
-get some basic output. Change into the ``$BASE`` directory and run::
-
- $ path/to/paste/scripts/paster serve
-
-This will run a server on http://localhost:8080
-
-.. comment (a psuedo-server for doctest)
-
- >>> set_default_app(make_app(BASE), 'http://localhost:8080')
- >>> show('/', 'default-frontpage')
-
-.. raw:: html
- :file: resources/TodoTutorial/default-frontpage.html
-
-You can run the server in different ways by passing command-line
-options to ``paster serve``, or by editing ``server.conf``. The
-default setup only serves connections from the local computer
-(use ``host='0.0.0.0'`` to allow connections from anywhere), and
-serves on port 8080 (use ``port = 80`` to make it the default
-server). However, this default server (WSGIUtils) is not suggested
-for production use. Twisted includes a better WSGI server, and there
-are a variety of ways to connect your application to Apache or
-IIS. We'll ignore those deployment issues now, though, and just use
-localhost:8080.
-
-
-Looking at servlets
--------------------
-
-The idea of a servlet in Paste is taken from Java, but the
-similarity isn't that great. Let's look at the ``index.py`` servlet
-we showed you:
-
-.. comment (create index.py highlighted)
-
- >>> show_file('web/index.py', 'before_editing')
-
-.. raw:: html
- :file: resources/TodoTutorial/web/index.py.before_editing.gen.html
-
-A few things to note:
-
-``from todo_sql.sitepage import SitePage``
- ``todo_sql`` is the package you just created, and ``sitepage`` is
- the ``sitepage.py`` file, and ``SitePage`` is a class.
- ``SitePage`` is where you'll put all your global customizations, a
- kind of mini-framework for your application.
-
-``class index(SitePage):``
- Every servlet must have a class with the same name as the file that
- contains it. This file gets created for every request, so you
- can assign attributes (like ``self.list`` or whatever) and they'll
- only last as long as one request.
-
-``def setup(self):``
- The ``setup`` method is called at the beginning of every request.
- This is where you should put together any objects that are
- required to process the request, and possibly perform actions
- (say, in response to a form submittal). The actual content will
- be rendered later.
-
-``self.options.vars``
- ``self.options`` is an object where you store data for use by your
- template. The template can access any attributes of the servlet,
- but assigning to ``self.options`` makes it explicit that the value
- is intended for the template.
-
-``self.request()``
- This is how, in the servlet, you access the request object. The
- request object has several methods we'll look at later -- the most
- useful being ``req.field(name, default)``, which retrieves the
- given field.
-
-``req.environ()``
- This is the WSGI environment. You won't know what that is, except
- that it's much like the environment passed to a CGI script. It
- contains things such as ``SCRIPT_NAME``, ``REMOTE_ADDR``, etc.
- It's a dictionary, so we're just getting a sorted list of the keys
- and values from that dictionary.
-
-The rest should be self-explanatory by now.
-
-Looking at templates
---------------------
-
-Now let's look at the template that goes with the servlet. Every
-template is in ``template/servlet_name.pt``. The ``.pt`` stands for
-"Page Template".
-
-.. comment (create index.py highlighted)
-
- >>> show_file('templates/index.pt', 'before_editing')
-
-.. raw:: html
- :file: resources/TodoTutorial/templates/index.pt.before_editing.gen.html
-
-OK, so a bit of explanation...
-
-``<html metal:use-macro="here/standard_template.pt/macros/page">``
- ``metal:use-macro`` says that this page will define slots that
- will be inserted into the ``page`` macro in
- ``standard_template.pt``. This is another way of saying that
- ``standard_template.pt`` gives the look and layout of the page.
- This is different than many templating systems, where you include a
- header and footer -- ``standard_template.pt`` can rearrange all
- your slots in whichever way it chooses, and is itself a complete
- page. We'll talk about slots next...
-
- Notice the ``metal:`` namespace. METAL is the Page Template
- "macro" system; TAL is the substitution system. Each uses special
- tags with the given namespace.
-
-``<metal:body fill-slot="body">``
- We could have any text we wanted before this tag and after
- ``<html>``, but for brevity we've left those out. If you were
- using a WYSIWYG tool it would *require* a valid page, so this is
- all quite compatible with WYSIWYG tools.
-
- ``fill-slot`` says that everything inside this tag will be
- inserted into the ``body`` slot of ``standard_template.pt``.
- Notice that we call the tag ``<metal:body>`` -- by putting it in
- the ``metal:`` namespace it will be eliminated from the final
- page, and we don't have to specify ``metal:fill-slot``. This is
- because the ``<body>`` tag is actually located in
- ``standard_template.pt``.
-
-``<tr tal:repeat="var options/vars">``
- ``tal:repeat`` is like a Python ``for`` loop. It means the
- ``<tr>`` tag will be repeated, looping over ``options/vars`` (and
- assigning to ``var``).
-
- ``options/vars`` isn't really a proper Python expression -- TAL
- has its own kind of shortened expressions. A ``/`` is a kind of
- generic accessor -- it can mean ``options.vars``,
- ``options['vars']`` or ``options.vars()``, depending on what kind
- of object ``options`` is. This is a way of hiding some of the
- complexity of accessing objects from template authors.
-
- Note also that each directive operates on the tag itself. So
- ``tal:repeat`` operates on the ``<tr>`` tag and all its contents.
-
-``<td tal:content="python: var[0]">Var Name</td>``
- This is a substitution -- ``tal:content`` replaces the contents of
- the tag with the given expression. The current contents (``Var
- Name``) are simply thrown away; they are there for documentation
- at most. If you wanted to leave them out, you could use the XHTML
- notation of ``<td tal:content="python: var[0]" />``
-
- Here we need a Python expression, because ``var/0`` would be like
- ``var['0']``, and we need to access the index zero, not ``"0"``.
- We indicate that it's a Python expression with the ``python:``
- prefix.
-
-That's a quick introduction to Page Templates. We'll wait to look at
-``standard_template.pt``.
-
-
-The sample application
-======================
-
-We'll be making a simple to-do list application. The application will
-have multiple lists, and each list has multiple items.
-
-
-Using a database
-----------------
-
-.. note::
-
- These SQLObject classes could be considered your "model", but
- really your model is whatever you want it to be. There's no
- formal concept of a model in this tutorial.
-
-The first thing we'll set up is a database connection. This example
-uses SQLObject_, which is a object-relational mapper. Basically, it
-makes your database tables look like Python classes, with each row in
-those tables as an instance of those classes.
-
-We'll be creating two tables:
-
-+-------------------------------+
-| todo_list |
-+=============+=================+
-| id | INT PRIMARY KEY |
-+-------------+-----------------+
-| description | TEXT |
-+-------------+-----------------+
-
-+---------------------------------+
-| todo_item |
-+==============+==================+
-| id | INT PRIMARY KEY |
-+--------------+------------------+
-| todo_list_id | INT NOT NULL |
-+--------------+------------------+
-| description | TEXT NOT NULL |
-+--------------+------------------+
-| done | BOOLEAN NOT NULL |
-+--------------+------------------+
-
-The actual types will differ somewhat depending on what database we'll
-be using. PostgreSQL, for instance, has a ``BOOLEAN`` data type, but
-on MySQL we'll just use ``INT``. SQLObject, however, handles all this
-for us. We just define the classes:
-
-.. _SQLObject: http://sqlobject.org
-
-.. comment (create db.py)
-
- >>> create_file('db.py', 'v1', r"""
- ... from sqlobject import *
- ...
- ... class TodoList(SQLObject):
- ...
- ... description = StringCol(notNull=True)
- ... items = MultipleJoin('TodoItem')
- ...
- ... class TodoItem(SQLObject):
- ...
- ... todo_list = ForeignKey('TodoList')
- ... description = StringCol(notNull=True)
- ... done = BoolCol(notNull=True, default=False)
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/db.py.v1.gen.html
-
-Some things to notice. First, all the symbols and classes you see in
-this example were imported with ``from sqlobject import *``.
-We create two new classes -- ``TodoList`` and ``TodoItem``.
-
-Each table is a class (that subclasses from ``SQLObject``). We use
-the normal naming style of classes (StudlyCaps), but SQLObject
-translates this to an underscore style for the database.
-
-Each column is an attribute of the class, using special classes to
-indicate the type (``StringCol``, ``BoolCol``, etc). Keyword
-arguments are used to indicate options such as whether ``NULL`` is
-allowed (by default it is), and whether there's a default value
-(SQLObject doesn't treat NULL as a default). You could also give the
-size of the text fields (by default they are all ``TEXT`` -- in these
-modern days it's not necessary to specify the length of your fields).
-
-SQLObject knows about several databases -- PostgreSQL, MySQL, and
-SQLite are especially well supported. It can hide much of the
-specifics of the database, including generating the ``CREATE``
-statement. We'll use SQLite in this example, but it's pretty trivial
-to use another backend.
-
-First, we have to add configuration to our ``server.conf`` file.
-We'll add these lines::
-
- import os
- database = 'sqlite:%s/data.db' % os.path.dirname(__file__)
-
-We use ``os.path.dirname(__file__)`` to put the database file in the
-same directory as ``server.conf``. You could also use something
-like::
-
- database = 'mysql://user:passwd@localhost/dbname'
- # or:
- database = 'postgresql://user:password@localhost/dbname'
-
-.. comment (change server.conf)
-
- >>> change_file('server.conf', [('insert', 2, r"""import os
- ... database = 'sqlite:%s/data.db' % os.path.dirname(__file__)
- ...
- ... """)])
-
-.. comment (put sqlobject-admin in path)
-
-Now we have to actually create the tables; we'll use the
-``sqlobject-admin`` script to help us with this. First::
-
-.. note::
-
- SQLObject is, among other things, a database abstraction layer. So
- it tries to use as many of the capabilities as it can of the
- underlying database, but it glosses over other issues. In this case,
- the ``todo_list_id`` column is a foreign key, but the SQL we show
- is for SQLite, and SQLite doesn't have foreign key constraints. On
- PostgreSQL the ``CREATE`` statement would look different.
-
-.. comment (Run it)
-
- >>> run('sqlobject-admin sql -f server.conf -m todo_sql.db')
- CREATE TABLE todo_item (
- id INTEGER PRIMARY KEY,
- done TINYINT NOT NULL,
- todo_list_id INT,
- description TEXT NOT NULL
- );
- CREATE TABLE todo_list (
- id INTEGER PRIMARY KEY,
- description TEXT NOT NULL
- );
-
-::
-
- $ sqlobject-admin sql -f server.conf -m todo_sql.db
- CREATE TABLE todo_item (
- id INTEGER PRIMARY KEY,
- done TINYINT NOT NULL,
- todo_list_id INT,
- description TEXT NOT NULL
- );
- CREATE TABLE todo_list (
- id INTEGER PRIMARY KEY,
- description TEXT NOT NULL
- );
-
-
-You have to be sure ``/var/www/example-builds`` is in your ``$PYTHONPATH``
-so that your ``todo_sql/`` directory is a module that Python can load. ``-m
-todo_sql.db`` tells ``sqlobject-admin`` to load that module, look for
-SQLObject classes, and use them for its command -- in this case
-(``sqlobject-admin sql``) showing the ``CREATE`` statements. Now, let's
-actually create the tables::
-
- $ sqlobject-admin create -f server.conf -m todo_sql.db
-
-.. comment (do it)
-
- >>> run('sqlobject-admin create -f server.conf -m todo_sql.db')
-
-It prints nothing on success. Or, use ``-v``, or even ``-vv``, to get
-more messages.
-
-Now, let's put in just a little data for later::
-
- $ sqlobject-admin execute -f server.conf "INSERT INTO
- todo_list (description) VALUES ('test 1')"
-
-.. comment (do it)
-
- >>> run('''sqlobject-admin execute -f server.conf "INSERT INTO
- ... todo_list (description) VALUES ('test 1')" ''')
-
-
-Creating a servlet
-------------------
-
-For now, we'll reuse the ``index.py`` servlet, adding this code:
-
-.. comment (make code)
-
- >>> create_file('web/index.py', 'v1', r"""
- ... from todo_sql.sitepage import SitePage
- ... from todo_sql.db import *
- ...
- ... class index(SitePage):
- ...
- ... def setup(self):
- ... self.options.title = 'List of Lists'
- ... self.options.lists = list(TodoList.select())
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/web/index.py.v1.gen.html
-
-There's one new item here::
-
- self.options.list = list(TodoList.select())
-
-``TodoList.select()`` creates a select query -- it *doesn't* actually
-access the database, but it will when we first iterate over it (like
-in a ``for`` loop). In this case, we use ``list()`` to turn it into a
-list. We could give arguments to ``.select()`` to add a ``WHERE``
-clause to the ``SELECT`` statement. Here's the servlet that goes with it:
-
-.. comment (make code)
-
- >>> create_file('templates/index.pt', 'v1', r"""
- ... <html metal:use-macro="here/standard_template.pt/macros/page">
- ... <metal:body fill-slot="body">
- ...
- ... <ul>
- ... <li tal:repeat="list options/lists">
- ... <a tal:attributes="href python: '%s/view_list?id=%s' % (servlet.baseURL, list.id)"
- ... tal:content="list/description">list</a></li>
- ... </ul>
- ...
- ... <p tal:condition="not: options/lists">
- ... There are no lists to display
- ... </p>
- ...
- ... <form action="./edit_list" method="POST">
- ... Create a new list:<br>
- ... <input type="hidden" name="id" value="new">
- ... <input type="hidden" name="_action_" value="save">
- ... <input type="text" name="description">
- ... <input type="submit" value="Create!!!!">
- ... </form>
- ... </metal:body>
- ... </html>
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/templates/index.pt.v1.gen.html
-
-We also have to add a little magic to ``web/__init__.py`` to configure
-SQLObject. (This will be improved in the future.)
-
-.. comment (make config)
-
- >>> create_file('web/__init__.py', 'v1', r"""
- ... import os
- ... from paste import wsgilib
- ... from paste.util.thirdparty import add_package
- ... add_package('sqlobject')
- ... import sqlobject
- ...
- ... sql_set = False
- ...
- ... def urlparser_hook(environ):
- ... global sql_set
- ... if not environ.has_key('todo_sql.base_url'):
- ... environ['todo_sql.base_url'] = environ['SCRIPT_NAME']
- ... if not sql_set:
- ... sql_set = True
- ... db_uri = environ['paste.config']['database']
- ... sqlobject.sqlhub.processConnection = sqlobject.connectionForURI(
- ... db_uri)
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/web/__init__.py.v1.gen.html
-
-And this is what we get:
-
-.. comment (show it)
-
- >>> change_file('__init__.py',
- ... [('insert', 5, "add_package('sqlobject')\n")])
- >>> show('/', 'v1-frontpage')
-
-.. raw:: html
- :file: resources/TodoTutorial/v1-frontpage.html
-
-edit_list page
---------------
-
-Now we have to create an ``edit_list`` page to create new lists.
-Here's what that looks like:
-
-.. comment (expanded)
-
- >>> create_file('web/edit_list.py', 'v1', r"""
- ... from todo_sql.sitepage import SitePage
- ... from todo_sql.db import *
- ...
- ... class edit_list(SitePage):
- ...
- ... def setup(self):
- ... self.list_id = self.request().field('id')
- ... if self.list_id != 'new':
- ... self.list = TodoList.get(int(self.list_id))
- ... self.options.title = 'Edit list '
- ...
- ... def actions(self):
- ... return ['save', 'destroy']
- ...
- ... def save(self):
- ... desc = self.request().field('description')
- ... if self.list_id == 'new':
- ... self.list = TodoList(description=desc)
- ... self.message('List created')
- ... else:
- ... self.list.description = desc
- ... self.message('List updated')
- ... self.sendRedirectAndEnd('./view_list?id=%s' % self.list.id)
- ...
- ... def destroy(self):
- ... desc = self.list.description
- ... self.list.destroySelf()
- ... self.message('List %s deleted' % desc)
- ... self.sendRedirectAndEnd('./')
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/web/edit_list.py.v1.gen.html
-
-A few things to point about out this. First, note we're using the
-edit form as a creation form, too, with the special id of ``"new"`` for
-this case. You don't have to do that, but I find it convenient.
-
-If there was an id passed in, we use it to fetch an instance of
-``TodoList`` with ``TodoList.get(int(self.list_id))``.
-
-Second, actions. When there's a field named ``_action_actionName`` or
-``_action_=actionName``, that tells the servlet to call the named
-action. (The first form is useful when you have a button where you
-can't necessarily give the action name the same value as the text of
-the button.) As a security measure, the ``.actions()`` method returns
-a list of possible actions -- so a user couldn't change the form and
-call an arbitrary method.
-
-``.setup()`` is called regardless of the action, and
-``.defaultAction()`` is later called if no action is defined. (By
-default, ``defaultAction`` doesn't do anything.) In the form that
-submits to ``edit_list``, we used an action of ``save``, so that's what
-gets called.
-
-In ``.save()`` there are two kinds of actions -- one inserts a row, and
-one updates a row. Insertion is like instance creation -- you call
-the class::
-
- self.list = TodoList(description=desc)
-
-Updating is like attribute assignment. (Remember, we already assigned
-``self.list`` in ``setup``.)
-
- self.list.description = desc
-
-Next, you'll see we call ``self.message(...)`` -- this stores a message
-in the user's session object. It's useful in cases like this, where
-you want to redirect the user someplace useful, but you also want to
-give them some indication of what happened. When they go to the next
-page, that message will be displayed at the top of the page (and then
-removed from the session). Even if you don't redirect, this is an easy
-way of adding messages to the top of the screen.
-
-Lastly, we do a redirect -- ``self.sendRedirectAndEnd()`` aborts the
-rest of the transaction and immediately redirects the user.
-
-You can also see we have a list deletion method (``destroy``). To
-delete a row with SQLObject, call
-``sqlobjectInstance.destroySelf()``. Then we just redirect back
-to the main page.
-
-Here's what happens when you create a list:
-
-.. comment (do it)
-
- >>> show('/edit_list?_action_=save&'
- ... 'id=new&description=another%20list', 'v1-edit')
-
-.. raw:: html
- :file: resources/TodoTutorial/v1-edit.html
-
-view_list page
---------------
-
-Hmm... Well, that last page isn't very interesting, is it... We'd better
-write that ``view_list`` servlet. Viewing a list means displaying all
-its items (``TodoItem``) and allowing items to be added, removed, and
-marked done or not done.
-
-.. comment (expanded)
-
- >>> create_file('web/view_list.py', 'v1', r"""
- ... from todo_sql.sitepage import SitePage
- ... from todo_sql.db import *
- ...
- ... class view_list(SitePage):
- ...
- ... def setup(self):
- ... self.options.list = TodoList.get(int(self.request().field('id')))
- ... self.options.list_items = list(self.options.list.items)
- ... self.options.title = 'List: %s' % self.options.list.description
- ...
- ... def actions(self):
- ... return ['check', 'add', 'destroy']
- ...
- ... def check(self):
- ... field = self.request().field
- ... for item in self.options.list.items:
- ... checked = field('item_%s' % item.id, False)
- ... if not checked and item.done:
- ... self.message('Item %s marked not done'
- ... % item.description)
- ... item.done = False
- ... if checked and not item.done:
- ... self.message('Item %s marked done'
- ... % item.description)
- ... item.done = True
- ... self.sendRedirectAndEnd(
- ... './view_list?id=%s' % self.options.list.id)
- ...
- ... def add(self):
- ... desc = self.request().field('description')
- ... if not desc:
- ... self.message('You must give a description')
- ... else:
- ... TodoItem(todo_list=self.options.list,
- ... description=desc)
- ... self.message('Item added')
- ... self.sendRedirectAndEnd(
- ... './view_list?id=%s' % self.options.list.id)
- ...
- ... def destroy(self):
- ... id = int(self.request.field('item_id'))
- ... item = TodoItem.get(id)
- ... assert item.todo_list.id == self.options.list.id, (
- ... "You are trying to delete %s, which does not "
- ... "belong to the list %s" % (item, self.options.list))
- ... desc = item.description
- ... item.destroySelf()
- ... self.message("Item %s removed" % desc)
- ... self.sendRedirectAndEnd(
- ... './view_list?id=%s' % self.options.list.id)
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/web/view_list.py.v1.gen.html
-
-Hopefully this looks familiar. In ``setup`` we load up the
-objects and set a title based on those options. We also fetch the
-list's items and define several actions: ``check`` marks items done
-or not done, ``add`` adds new items, and ``destroy`` removes a single
-item.
-
-Next we need the template:
-
-.. comment (template)
-
- >>> create_file('templates/view_list.pt', 'v1', r"""
- ... <html metal:use-macro="here/standard_template.pt/macros/page">
- ... <metal:body fill-slot="body">
- ...
- ... <form action="view_list?_action_=check" method="POST">
- ... <input type="hidden" name="id"
- ... tal:attributes="value options/list/id">
- ...
- ... <table>
- ... <tr>
- ... <th>Done?</th>
- ... <th>Description</th>
- ... </tr>
- ...
- ... <tr tal:repeat="item options/list_items">
- ... <tal:let define="name python: 'item_%s' % item.id">
- ... <td>
- ... <input type="checkbox"
- ... tal:attributes="name name;
- ... id name;
- ... checked item/done">
- ... </td>
- ... <td>
- ... <label tal:attributes="for name"
- ... tal:content="item/description">description</label>
- ... </td>
- ... <td><a tal:attributes="
- ... href python: 'view_list?_action_=destroy&item_id=%s'
- ... % item.id">[delete]</a></td>
- ... </tal:let>
- ... </tr>
- ...
- ... <tr tal:condition="not: options/list_items">
- ... <td colspan=2>No items to display</td>
- ... </tr>
- ...
- ... </table>
- ...
- ... <input type="submit" value="Update item status">
- ... </form>
- ...
- ... <form action="view_list?_action_=add" method="POST">
- ... <input type="hidden" name="id"
- ... tal:attributes="value options/list/id">
- ... Add item:<br>
- ... <input type="text" style="width: 100%" name="description"><br>
- ... <input type="submit" value="Add">
- ... </form>
- ...
- ... <form action="edit_list?_action_=destroy" method="POST">
- ... <input type="hidden" name="id"
- ... tal:attributes="value options/list/id">
- ... <input type="submit" value="Delete this list"
- ... onclick="return window.confirm('Really delete this list?')">
- ... </form>
- ...
- ... </metal:body>
- ... </html>
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/templates/view_list.pt.v1.gen.html
-
-Now we can view that list:
-
- >>> show('/view_list?id=2', 'v1-view')
-
-.. raw:: html
- :file: resources/TodoTutorial/v1-view.html
-
-You can play with the app, but we're pretty much done.
-
-
-Changing standard_template.pt
------------------------------
-
-The one thing missing is navigation, which we want to apply globally
-to our application. To fix this, we'll be changing ``SitePage`` and
-``standard_template.pt``.
-
-``SitePage`` is the class that all our other servlets inherit from.
-By adding things to ``SitePage.awake()`` we make them available to all
-of our application. You can also hang utility methods off this class,
-or anything else that should be globally available, or have some
-default (but overrideable) implementation.
-
-For navigation we're going to put all the lists in a sidebar. So
-we'll have to load all the lists in ``SitePage.awake()``. Then we'll
-use that data in ``standard_template.pt``. We'll add one line to
-``SitePage.awake``, before ``self.setup()``::
-
- self.options.lists = list(TodoList.select())
-
-We can get rid of this line in ``web/index.py`` now as well, since it
-will be redundant.
-
-.. comment (change files)
-
- >>> change_file('sitepage.py', [
- ... ('insert', 4, 'from todo_sql.db import *\n'),
- ... ('insert', 20,
- ... " self.options.lists = list(TodoList.select())\n")])
- >>> change_file('web/index.py', [
- ... ('delete', 7, 8)])
-
-Now we'll change ``standard_template.pt`` to create navigation based
-on this:
-
-.. comment (make it)
-
- >>> create_file('templates/standard_template.pt', 'nav', r"""
- ... <metal:tpl define-macro="page">
- ... <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
- ... "http://www.w3.org/TR/REC-html40/loose.dtd">
- ... <html lang="en-US">
- ... <head>
- ... <title tal:content="servlet/title">title</title>
- ... <link rel="stylesheet" type="text/css"
- ... tal:attributes="href python: servlet.baseStaticURL + '/stylesheet.css'">
- ...
- ... <metal:slot define-slot="extra_head"></metal:slot>
- ...
- ... </head>
- ... <body>
- ...
- ... <h1 id="header"><tal:mark replace="servlet/title"/></h1>
- ...
- ... <table width="100%">
- ... <tr>
- ... <td id="sidenav" width="20%">
- ... <a tal:attributes="href servlet/baseURL">Home</a><br>
- ... <tal:for repeat="list options/lists">
- ... <a tal:attributes="href python: '%s/view_list?id=%s' %
- ... (servlet.baseURL, list.id)"
- ... tal:content="list/description">link</a><br>
- ... </tal:for>
- ... </td>
- ...
- ... <td>
- ... <span tal:replace="structure servlet/messageText">
- ... This is where the notification messages go.
- ... </span>
- ...
- ... <div id="content">
- ... <metal:tpl define-slot="body">
- ... [This page has not customized its "body" slot]
- ... </metal:tpl>
- ... </div>
- ...
- ... </td></tr></table>
- ...
- ... </body>
- ... </html>
- ... </metal:tpl>
- ... """)
-
-.. raw:: html
- :file: resources/TodoTutorial/templates/standard_template.pt.nav.gen.html
-
-Let's look at that view page again:
-
-.. comment (show)
-
- >>> show('/view_list?id=2', 'v2-view')
-
-.. raw:: html
- :file: resources/TodoTutorial/v2-view.html
-
-Conclusion
-==========
-
-Now that we're finished, you might want to play with the result of the
-tutorial and extend it. You'll find the application in
-``examples/todo_sql``.
-
-I also want to note a few aspects of what we've created:
-
-* Style is separate from logic, by way of templates (style) and
- servlets (controller logic). This is similar to a Controller-View
- separation, but it's also intended to assist a separation of roles,
- typically Programmer-Designer.
-
-* The web interface logic is separated from the domain (or business)
- logic. We actually don't have much domain logic in this program --
- just the stuff in ``db.py`` -- but larger applications would have
- more. None of this logic needs to be specific to the web at all, or
- the framework we are using.
-
-* These distinctions between web logic, style decisions, and domain
- logic are represented (a bit casually) by the file layout.
-
-* All the templates are valid HTML (and could be valid XHTML if you
- want to write them like that -- but it's not required). They can be
- viewed, unrendered, in a browser. The templates can be almost
- arbitrarily complicated, depending on how complicated your view
- logic is -- but all the logic is kept together in a relatively small
- number of files. If you want to share logic across pages, METAL can
- be used for more than just ``standard_template.pt``.
-
diff --git a/docs/blog-tutorial.txt b/docs/blog-tutorial.txt
deleted file mode 100644
index d0931e9..0000000
--- a/docs/blog-tutorial.txt
+++ /dev/null
@@ -1,374 +0,0 @@
-+++++++++++++
-Blog Tutorial
-+++++++++++++
-
-:author: Ian Bicking <ianb@colorstudy.com>
-:revision: $Rev$
-:date: $LastChangedDate$
-
-.. contents::
-
-.. note::
-
- This tutorial is not yet finished. What you see is what you get,
- and yeah that's not a whole lot.
-
-Introduction
-============
-
-This tutorial will go through the process of creating a blog using
-`Python Paste <http://pythonpaste.org>`_, SQLObject_, and `Zope Page
-Templates`_. This blog will rely heavily on static publishing -- that
-is, when at all possible flat HTML pages will be written to disk. For
-some parts (e.g., posting a new item) this will of course be
-infeasible, but for most of the site this should work fine.
-
-.. _SQLObject: http://sqlobject.org
-.. _Zope Page Templates: http://www.zope.org/DevHome/Wikis/DevSite/Projects/ZPT/FrontPage
-
-As much as possible, this code will be accompanied by unit tests, and
-test-driven methodologies. Doing test-driven documenting of the
-incremental process of creating test-driven software may get a little
-hairy, but wish me luck!
-
-This tutorial presupposes you are somewhat comfortable with the basic
-stack -- the `To-Do Tutorial`_ is a better place to start for a
-beginner.
-
-.. _To-Do Tutorial: TodoTutorial.html
-
-Setting Up The App
-==================
-
-.. note::
-
- We're doing all this in the Python interpreter, even though you'd normally do
- some of this in the shell. This way the authors of this tutorial
- can use something called doctest_, which allows this tutorial to
- be tested Python in an automated way.
-
- .. _doctest: http://python.org/doc/current/lib/module-doctest.html
-
-.. comment:
-
- >>> from paste.tests.doctest_webapp import *
- >>> BASE = '/var/www/example-builds/wwblog'
- >>> import sys
- >>> clear_dir(BASE)
- >>> run("paster create --template=webkit_zpt %s" % BASE)
- >>> os.chdir(BASE)
-
-::
-
- $ export PYTHONPATH=/path/to/Paste:$PYTHONPATH
- $ BASE=/var/www/example-builds/wwblog
- $ paster create --template=webkit_zpt $BASE
- $ cd $BASE
-
-The Model
-=========
-
-Since we're using SQLObject, we'll be doing the complete model in
-that. The predecessor of this blog used flat files, custom-written
-indexes, and simple rfc822_ based files for structure. It did not
-scale well at all.
-
-.. _rfc822: http://python.org/doc/current/lib/module-rfc822.html
-
-Here's the model:
-
-.. run:
-
- create_file('db.py', 'v1', r"""
- from sqlobject import *
-
- class Article(SQLObject):
- url = StringCol(notNull=True)
- title = StringCol()
- content = StringCol(notNull=True)
- content_mime_type = StringCol(notNull=True)
- author = ForeignKey('User')
- parent = ForeignKey('Article', default=None)
- created = DateTimeCol(notNull=True, default=DateTimeCol.now)
- last_updated = DateTimeCol(default=None)
- atom_id = StringCol()
- hidden = BoolCol(notNull=True, default=False)
- article_type = StringCol(notNull=True, default='article')
- categories = RelatedJoin('Category')
-
- class Category(SQLObject):
- name = StringCol(alternateID=True)
- articles = RelatedJoin('Article')
-
- class User(SQLObject):
- class sqlmeta:
- table = 'user_info'
- username = StringCol(alternateID=True)
- email = StringCol()
- name = StringCol()
- homepage = StringCol()
- password_encoded = StringCol()
- role = StringCol(notNull=True, default='user')
- """)
-
-.. raw:: html
- :file: resources/blog-tutorial/db.py.v1.gen.html
-
-A few things to note:
-
-* All the columns allow ``NULL`` by default, unless we say
- ``notNull=True``.
-
-* ``ForeignKey('User')`` is a join to another table (the ``User``
- table, of course). We have to use strings to refer to other class,
- because in this case the ``User`` class hasn't even been created.
- Generally all references between classes are by name.
-
-* ``created`` has a default. You can give a fixed default (like
- ``True`` or ``3``), or you can pass in a function that is called.
- In this case, if you don't indicate ``Article(...,
- created=something)`` then ``created`` will be the current date and
- time. Unless a default is explicitly given, it is an error to leave
- a column out of the constructor. ``NULL`` (which is ``None`` in
- Python) is *not* considered a default.
-
-* Some column types don't relate directly to database types. For
- instance, though PostgreSQL has a ``BOOLEAN`` type, most databases
- don't, so ``BoolCol`` translates to some kind of ``INT`` column on
- those database.
-
-* ``RelatedJoin('Category')`` creates a mapping table
- (``article_category``) and is a many-to-many join between articles
- and categories.
-
-* ``user`` isn't a valid table name in many databases, so while the
- class is named ``User``, the table actually is ``user_info``. This
- kind of extra information about a class is typically passed in
- through the ``sqlmeta`` inner class.
-
-These classes have lots of other *behavior*, but this should be a good
-list of actual information. We'll add more behavior later.
-
-Now we'll create the database. First we configure it, adding these
-lines to ``server.conf``::
-
- import os
- database = 'sqlite:%s/data.db' % os.path.dirname(__file__)
-
-You could also use::
-
- database = 'mysql://user:passwd@localhost/dbname'
- database = 'postgresql://user:password@localhost/dbname'
-
-.. comment (change server.conf)
-
- >>> change_file('server.conf', [('insert', 2, r"""import os
- ... database = 'sqlite:%s/data.db' % os.path.dirname(__file__)
- ...
- ... """)])
-
-Now we'll use ``sqlobject-admin`` to set up the tables:
-
-.. comment (do it)
-
- >>> run_command('sqlobject-admin create -f server.conf '
- ... '-m wwblog.db', 'create', and_print=True)
-
-
-.. raw:: html
- :file: resources/blog-tutorial/shell-command.create.gen.html
-
-Fixture Data
-------------
-
-To test things later we'll need a bit of data to make the tests
-interesting. It's best if we write code to clear any data and put
-known data in -- that way we can restore the database at any time to a
-known state, and can write our tests against that data.
-
-We'll add some code to the end of ``db.py``:
-
-.. comment (change)
-
- >>> append_to_file('db.py', 'append-fixture', r"""
- ...
- ... from paste import CONFIG
- ... def reset_data():
- ... sqlhub.processConnection = connectionForURI(CONFIG['database'])
- ... for soClass in (User, Category, Article):
- ... soClass.clearTable()
- ... auth = User(username='author', email='author@example.com',
- ... name='Author Person', password_encoded=None,
- ... role='author', homepage=None)
- ... user = User(username='commentor', email='comment@example.com',
- ... name='Comment Person', password_encoded=None,
- ... role='user', homepage='http://yahoo.com')
- ... programming = Category(name='Programming')
- ... family = Category(name='family')
- ... a1 = Article(url='/2004/05/01/article1.html',
- ... title='First article',
- ... content='This is an article',
- ... content_mime_type='text/html',
- ... author=auth, parent=None,
- ... last_updated=None, atom_id=None)
- ... a2 = Article(url='/2004/05/10/article2.html',
- ... title='Second article',
- ... content='Another\narticle',
- ... content_mime_type='text/plain',
- ... author=auth, parent=None,
- ... last_updated=None, atom_id=None)
- ... c1 = Article(url='/2004/05/01/article1-comment1.html',
- ... title=None, content='Nice article!',
- ... content_mime_type='text/x-untrusted-html',
- ... author=user, parent=a1, last_updated=None,
- ... atom_id=None, article_type='comment')
- ... a1.addCategory(programming)
- ... a1.addCategory(family)
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/db.py.append-fixture.gen.html
-
-Test Fixture
-------------
-
-See `Testing Applications With Paste <testing-applications.html>`_ for
-more on the details of how we set up testing. We'll be using `py.test
-<http://codespeak.net/py/current/doc/test.html>`_ for the testing
-framework.
-
-First, lets create our own test fixture. We'll create a directory
-``tests/`` and add a file ``fixture.py``.
-
-.. comment (create files):
-
- >>> create_file('tests/__init__.py', 'v1', '#\n')
- >>> create_file('tests/fixture.py', 'v1', r"""
- ... from paste.tests.fixture import setup_module as paste_setup
- ... from wwblog import db
- ...
- ... def setup_module(module):
- ... paste_setup(module)
- ... db.reset_data()
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/tests/fixture.py.v1.gen.html
-
-Now in each test we'll do::
-
- from wwblog.tests.fixture import setup_module
-
-And that will give us a consistent state for the module (note that
-data isn't reset between each test in the module, just once for the
-module, so we'll have to be aware of that).
-
-Let's write a first test:
-
-.. comment (create test):
-
- >>> create_file('tests/test_db.py', 'v1', r"""
- ... from fixture import setup_module
- ... from wwblog.db import *
- ...
- ... def test_data():
- ... # make sure we have the two users we set up
- ... assert len(list(User.select())) == 2
- ... # and get the first article for testing
- ... a1 = list(Article.selectBy(title='First article'))[0]
- ... # make sure it has categories, then make sure the
- ... # categories contain this article as well
- ... assert len(list(a1.categories)) == 2
- ... for cat in a1.categories:
- ... assert a1 in cat.articles
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/tests/test_db.py.v1.gen.html
-
-For the most part, this stuff is already tested by SQLObject, but this
-is a basic sanity check, and a test that we have set up the classes
-properly. One problem, though, is that we have to make sure that
-``sys.path`` is set up properly. We could set ``$PYTHONPATH``, but
-that can be a bit annoying; we'll put it in a special file
-``conftest.py`` that py.test loads up:
-
-.. comment (create):
-
- >>> create_file('conftest.py', 'v1', r"""
- ... import sys, os
- ... sys.path.append('/path/to/Paste')
- ... sys.path.append(os.path.dirname(os.path.dirname(__file__)))
- ... from paste.util.thirdparty import add_package
- ... add_package('sqlobject')
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/conftest.py.v1.gen.html
-
-Now we should be able to run py.test:
-
-.. comment (do):
-
- >>> run_command('py.test', 't1', and_print=True)
- inserting into sys.path: ...
- =...= test process starts =...=
- testing-mode: inprocess
- executable: .../python (...)
- using py lib: .../py <rev ...>
- .../test_db.py[1] .
- =...= tests finished: 1 passed in ... seconds =...=
-
-.. raw:: html
- :file: resources/blog-tutorial/shell-command.t1.gen.html
-
-Very good! Alright then, on to making an application...
-
-Static Publishing
------------------
-
-Remember I said something about static publishing? So... what does
-that mean?
-
-Well, it means that when possible we should write files out to disk.
-These files might be in their final form, though in some environments
-it might be nice to write out files that are interpreted by `server
-side includes <http://httpd.apache.org/docs/mod/mod_include.html>`_ or
-PHP.
-
-By generating what is effectively code, the "static" files can contain
-dynamic portions, e.g., a running list of recently-updated external
-blogs. But more importantly, many changes can be made without
-generating the entire site; changes to the look of the site, of
-course, but also smaller things, like up-to-date archive links on the
-sides of pages and other little bits of code.
-
-This tutorial will work with server-side includes, because they are
-dumb enough that we won't be tempted to push too much functionality
-into them (and we might be able to extract their functionality into
-fully-formed pages); but they'll also save us a lot of work early on.
-If you haven't used server-side includes you can read the document I
-linked too, but you can also probably pick it up easily enough from
-the examples.
-
-URL Layout
-----------
-
-The blog "application" will be available through some URL (which we
-can figure out at runtime). But everything else gets written onto
-disk with some URL equivalent, so that path and URL will have to be
-configurable.
-
-The Front Page
---------------
-
-First we'll set up a simple front page. We'll write a new
-``index.py``:
-
-.. comment (do so):
-
- >>> create_file('web/index.py', 'v1', r"""
- ... """)
-
-.. raw:: html
- :file: resources/blog-tutorial/web/index.py.v1.gen.html
diff --git a/docs/index.txt b/docs/index.txt
index 039202f..64b640b 100644
--- a/docs/index.txt
+++ b/docs/index.txt
@@ -1,35 +1,131 @@
Documentation
=============
-Reference
----------
-
-* `Paste Reference Documentation
- <http://pythonpaste.org/docs/reference.html>`_
-* `paste.deploy <http://pythonpaste.org/deploy/paste-deploy.html>`_
-* `Configuration <configuration.html>`_
-* `Integrating Frameworks Info Paste <integration.html>`_
-* `Roadmap for Paste <roadmap.html>`_
-* `Testing Applications With Paste <testing-applications.html>`_
-* `URL Parsing with WSGI and Paste <url-parsing-with-wsgi.html>`_
-
Tutorials and Introduction
---------------------------
+==========================
+
+* `To-Do Tutorial <http://pythonpaste.org/todo/TodoTutorial.html>`_
+
+The core Paste package is a set of tools that use `WSGI
+<http://www.python.org/peps/pep-0333.html>`_ heavily as the means of
+communication. WSGI is an in-process CGI-like communication protocol.
+
+Right now the best way to see the various tools available in Paste is
+to `look through the source <module-index.html>`_.
-* `To-Do Tutorial <TodoTutorial.html>`_
-* `What Is Paste? <what-is-paste.html>`_; older related documents:
+Related Projects
+================
- * `What is WSGIKit <http://blog.ianbicking.org/what-is-wsgikit.html>`_
- * `What can WSGIKit do for you?
- <http://blog.ianbicking.org/what-can-wsgikit-do-for-you.html>`_
- * Ian Bicking presented at `PyCon 2005 <http://pycon.org/dc2005>`_ on
- `WSGI Middleware and WSGIKit
- <http://www.imagescape.com/software/docs/wsgikit-pycon2005/>`_:
- *Using WSGI Middleware to build a foundation for Python web
- programming*
+Closely related packages:
-Developers
-----------
+* `Paste Deploy <http://pythonpaste.org/deploy/>`_
+* `Paste Script <http://pythonpaste.org/script/>`_
+* `Paste WebKit <http://pythonpaste.org/webkit/>`_
+* `Wareweb <http://pythonpaste.org/wareweb/>`_
+
+Developer Documentation
+=======================
* `Developer Guidelines <DeveloperGuidelines.html>`_
* `Style Guide <StyleGuide.html>`_
+
+Reference Documentation
+=======================
+
+* `Testing Applications With Paste <testing-applications.html>`_
+* `URL Parsing with WSGI and Paste <url-parsing-with-wsgi.html>`_
+
+
+License
+=======
+
+Paste is distributed under the `Python Software Foundation`__
+license. This is a BSD/MIT-style license.
+
+.. __: http://www.python.org/psf/license.html
+
+Overview
+========
+
+If you don't want to look through source, here's a quick overview of
+what there is here:
+
+Testing
+-------
+
+* A fixture for testing WSGI applications conveniently and in-process,
+ in ``paste.fixture``
+
+* A fixture for testing command-line applications, also in
+ ``paste.fixture``
+
+* Check components for WSGI-compliance in ``paste.lint``
+
+Dispatching
+-----------
+
+* Chain and cascade WSGI applications (returning the first non-error
+ response) in ``paste.cascade``
+
+* Dispatch to several WSGI applications based on URL prefixes, in
+ ``paste.urlmap``
+
+* Allow applications to make subrequests and forward requests
+ internally, in ``paste.recursive``
+
+Web Application
+---------------
+
+* Run CGI programs as WSGI applications in ``paste.cgiapp`` (and
+ Python-sepcific CGI programs with ``paste.pycgiwrapper``)
+
+* Traverse files and load WSGI applications from ``.py`` files (or
+ static files), in ``paste.urlparser``
+
+* Serve static directories of files, also in ``paste.urlparser``
+
+Tools
+-----
+
+* Catch HTTP-related exceptions (e.g., ``HTTPNotFound``) and turn them
+ into proper responses in ``paste.httpexceptions``
+
+* Check for signed cookies for authentication, setting ``REMOTE_USER``
+ in ``paste.login``
+
+* Create sessions in ``paste.session`` and ``paste.flup_session``
+
+* Gzip responses in ``paste.gzip``
+
+* A wide variety of routines for manipulating WSGI requests and
+ producing responses, in ``paste.wsgilib``
+
+Debugging Filters
+-----------------
+
+* Catch (optionally email) errors with extended tracebacks (using
+ Zope/ZPT conventions) in ``paste.exceptions``
+
+* Catch errors presenting a `cgitb
+ <http://python.org/doc/current/lib/module-cgitb.html>`_-based
+ output, in ``paste.cgitb_catcher``.
+
+* Profile each request and append profiling information to the HTML,
+ in ``paste.profilemiddleware``
+
+* Capture ``print`` output and present it in the browser for
+ debugging, in ``paste.printdebug``
+
+* Validate all HTML output from applications using the ``WDG Validator
+ <http://www.htmlhelp.com/tools/validator/>`_, appending any errors
+ or warnings to the page, in ``paste.wdg_validator``
+
+Other Tools
+-----------
+
+* A file monitor to allow restarting the server when files have been
+ updated (for automatic restarting when editing code) in
+ ``paste.reloader``
+
+* A class for generating and traversing URLs, and creating associated
+ HTML code, in ``paste.url``
diff --git a/docs/roadmap.txt b/docs/roadmap.txt
deleted file mode 100644
index bbfbbdd..0000000
--- a/docs/roadmap.txt
+++ /dev/null
@@ -1,208 +0,0 @@
-Paste Roadmap
-+++++++++++++
-
-:revision: $Rev$
-:date: $LastChangedDate$
-
-This is a document describing Paste's future and some of the things
-planned. This may change as events continue. Discussion is welcome;
-please direct discussion to paste-users@pythonpaste.org
-
-Bold items are particularly important. The sections are in no
-particular order.
-
-.. contents::
-
-To-Do
-=====
-
-paster
-------
-
-* **Package installation and upgrading.** (partially done)
-
- * Package installation should allow for Paste-specific hooks.
-
-* Allow applications to easily add their own commands, e.g.,
- ``add-user``.
-
-* Better PID management when in daemon mode (don't clobber a PID file
- of a running process)
-
-* Add options to manage the daemon process, like force
- reloading/restarting (HUP?) and stopping; make init.d scripts as
- minimal as possible.
-
-paste.docsupport
-----------------
-
-* Add indexing for the various objects (middleware, configuration
- options, WSGI environment keys).
-
-* Extract some packages into separate files. (e.g., ``paste.webkit``,
- ``paste.wareweb`` -- but *not* ``paste.exceptions``)
-
-paste.tests.doctest_webapp
---------------------------
-
-* Maybe move out of ``tests`` package.
-
-* Make it fail hard on certain errors (e.g., when ``paster`` fails)
-
-paste.tests.fixture
--------------------
-
-* Maybe move out of ``tests`` package.
-
-* Get rid of fake_request (convert current tests to TestApp).
-
-* Fill out or remove other mock objects. (Maybe make a package for
- other pieces?)
-
-* ``setup_module`` really only applies to application acceptance
- testing, but can be enabled unwittingly when doing ``import *`` --
- figure something out there.
-
-paste.pycgiwrapper
-------------------
-
-* Document, test, or throw away.
-
-* Allow for non-Python CGI scripts (no clever tricks, just run it in a
- subprocess).
-
-paste.pyconfig
---------------
-
-* Put in notion of "profiles," which involve different sets of
- configuration files.
-
-* Add ini-file parser/loader (probably taking from the old
- ``paste.conf`` module), so some parts of configuration can be
- explicitly handed off to a simpler configuration file.
-
-* Clean up ``use_package`` and move to another module.
-
-paste.reloader
---------------
-
-* Make it optionally use FAM or iNotify, instead of polling files.
-
-* Make sure it uses HUP, etc.
-
-paste.wdg_validate
-------------------
-
-* Allow other validators to be plugged in.
-
-* Validate CSS (embedded CSS with report in parent page?).
-
-Blue-sky
-~~~~~~~~
-
-* Validate Javascript...? An anal Javascript parser would be really
- cool. Maybe use http://www.crockford.com/javascript/lint.html --
- the interesting part is including the output of the lint in pages
- that include the Javascript. Since the lint program itself is
- written in Javascript, this is mostly a figure-it-out task, not a
- server infrastructure task.
-
-paste.exceptions
-----------------
-
-* Display local variables in exception reports, like ``cgitb``.
- Except values should be inlined into the source line itself in HTML
- ``title`` attributes, and the ``class`` should represent the rough
- type (e.g., module, None, string, number, function).
-
-* Keep extra data from being doubly displayed.
-
-* Add object and protocol so HTML can be included in extra data.
-
-* Include "serial number" in non-exception output.
-
-* Add hooks to suppress reporters. E.g., if a "developer" is logged
- in, the error report need not mailed. Maybe just based on IP
- address (a rough feature is all that is needed).
-
-Blue-sky
-~~~~~~~~
-
-* Allow evaluation in local stacks, interactively, keeping the
- traceback in a session or similar location.
-
-paste.urlparser
----------------
-
-* Static-file-only serving, for serving directories full of Javascript
- and whatnot without security concern.
-
-* Middleware for various compositions of applications, like nesting
- several applications under the same URL space, using the first one
- that doesn't return 404.
-
-paste.session
--------------
-
-* **Use flup session middleware**
-
-* Add DB-API-based session storage.
-
-* Add URI->DB-API connection parser (maybe strip from SQLObject).
-
-paste.login
------------
-
-* Really document it, and test it.
-
-* Provide some useful backends (htpasswd, database/DB-API,
- SQLObject).
-
-paste.gzipper
--------------
-
-* Probably should just be removed.
-
-Things to Add
-=============
-
-No module yet, so these are listed generally.
-
-* Really good static-file WSGI application, that knows about
- If-Modified-Since, ETag, etc. Should have caching of gzipped
- versions and cache some metadata (like etags themselves).
-
-* Authorization middleware.
-
-* Something to manage collections of static files, like a Javascript
- library.
-
-* Something to copy static files to a known location, based on
- configuration. E.g., copy static files into a location served by
- Apache.
-
-* Bring some Javascript library in, mostly for use in examples, but
- also as a kind of suggestion.
-
-* Upload-progress-tracking WSGI application.
-
-* Something like Webware's TaskKit, but more abstract. And
- multiprocess-friendly.
-
-Done
-====
-
-25 May 2005:
- **``--daemon`` option for running as background process**
-
-29 May 2005:
- * Template files should have optional ``.ext_tmpl`` extension, where
- ``_tmpl`` gets stripped. This way the currently-invalid ``.py``
- template files wouldn't be recognized as Pythohn files.
-
-30 May 2005:
-
- * **Deployment of multiple applications using configuration files**
-
- * An application could be mapped to a particular URL, given a
- configuration file that describes the application.
diff --git a/docs/what-is-paste.txt b/docs/what-is-paste.txt
deleted file mode 100644
index b55df9f..0000000
--- a/docs/what-is-paste.txt
+++ /dev/null
@@ -1,177 +0,0 @@
-What Is Paste?
-==============
-
-:author: Ian Bicking
-:revision: $Rev$
-:date: $LastChangedDate$
-
-Introduction
-------------
-
-It has come up several times that people don't understand quite what
-Paste is and what it is intended to be. This document is an attempt
-to respond to that.
-
-In part the confusion has been because Paste has is really several
-things. It is an attempt to fill in some of the gaps in web
-frameworks, and to identify places where things can be shared; as such
-it is a reaction to the current state of frameworks, and a direct
-attempt to be complimentary to those frameworks. As a result it can
-be somewhat eclectic.
-
-Server/Application Glue
------------------------
-
-`WSGI <http://www.python.org/peps/pep-0333.html>`_ defines how servers
-invoke applications, and how application respond. However, it does
-not define how servers or applications come into existance, or how
-they are passed to each other.
-
-Paste is meant to bridge that, but providing a single entry point that
-can create and configure a server, create an WSGI application, and
-hook the two together.
-
-This generally involves distinct code for each server supported, since
-there isn't any standard.
-
-Also, Paste is expected to create the applications that get served.
-This is typically done through at least somewhat-custom code that
-is driven by the configuration. Which leads us to...
-
-Configuration
--------------
-
-In order to set up servers and application, some kind of configuration
-is needed. Paste loads up configuration files and makes these
-available to all parts of the system.
-
-One goal of Paste is to support small pieces of decoupled code that
-work together. This is part of its WSGI-driven architecture.
-However, exactly how that code is split up is an implementation detail
-that really shouldn't be exposed to end users. Because of this, each
-component can't have its own configuration without resulting in a mess
-of configuration files and formats that are fragile and difficult to
-understand.
-
-This configuration is accessible from all portions of the system, so
-your application configuration can go in beside server and middleware
-configuration.
-
-Reusable Middleware
--------------------
-
-WSGI allows for the idea of "middleware" -- something that is both a
-server and an application. This is similar to a filter or a wrapper.
-By building these on WSGI, they are neutral with respect to any
-particular framework.
-
-Use of the middleware is generally optional, but they serve as a way
-to share work, and tend to be a fairly good architecture for many
-problems.
-
-Some of the middleware included:
-
-* Adding configuration information to the request
- (``paste.configmiddleware``)
-
-* Catching and reporting errors (``paste.error_middleware``)
-
-* Catching HTTP-related exceptions and producing HTTP responses
- (``paste.httpexceptions``)
-
-* Testing for WSGI compliance (``paste.lint``)
-
-* Identifying and authenticating user logins (``paste.login``)
-
-* Facilitating internal redirects and recursive calls
- (``paste.recursive``)
-
-* Adding sessions to the request (``paste.session``)
-
-* Validating HTML output from applications (``paste.wdg_validate``)
-
-Another kind of middleware is one which finds and constructs
-applications. At the moment, just one such middleware is in the
-library: ``paste.urlparser``. This looks on disk for files and Python
-modules, and creates WSGI applications from them. Other URL resolvers
-are also possible, e.g., one that traverses objects, or uses explicit
-URL->application maps.
-
-Otherwise Homeless Code
------------------------
-
-All code has to go somewhere. Sometimes there's not a good location
-for that code. So it can go in Paste.
-
-An Implementation Webware
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-``paste.webkit`` is a reimplementation of Webware built on the Paste
-middleware. This is a fairly thin implementation, mostly mapping the
-middleware APIs to Webware APIs.
-
-In this system Webware Servlet instances become WSGI applications
-(``paste.webkit.wkservlet.Servlet.__call__`` implements a WSGI
-application).
-
-Reloader
-~~~~~~~~
-
-This module (``paste.reloader``) checks in the background for modified
-files (typically modules in ``sys.modules``) and restarts the entire
-server when that happens.
-
-This avoids the stale-code issue, where code in memory gets out of
-sync with code on disk. When that happens confusion can ensue.
-Manually restarting is also somewhat annoying, so this does the
-restarting for you. It's not really appropriate for a live
-deployment, but it works well in development.
-
-Documentation System
-~~~~~~~~~~~~~~~~~~~~
-
-This is still young and not well defined, but there's some work on
-using `doctest
-<http://python.org/doc/current/lib/module-doctest.html>`_ to generate
-and test documentation. These can turn into a kind of acceptance
-test.
-
-Application Templates
----------------------
-
-One facility in Paste is ``paster`` a script to create
-application "templates". Basically an empty application, with a
-little structure. For instance, the Webware/Zope Page Template
-(webkit_zpt) application template sets up these files::
-
- __init__.py
- server.conf
- sitepage.py
- templates/standard_template.pt
- templates/index.pt
- web/__init__.py
- web/index.py
- web/static/stylesheet.css
-
-This is a kind of a minimal set up for a typical web application using
-these systems. After the application is set up, ``paster`` can
-provide other commands. For instance in a webkit_zpt application
-``paster servlet edit`` will create ``web/edit.py`` and
-``web/edit.pt`` files. Each template has control implement any
-commands how it sees fit, but some convenient functions and classes
-are provided to make implementation easier.
-
-Distribution
-------------
-
-This is still an open issue, but I hope Paste will facilitate
-installation of multiple frameworks quickly. Some of this is handled
-already: ``paste-server`` starts a server easily and quickly, and
-``paster`` gives a user the basis for an application quickly.
-Actual software installation is a little harder. Right now the plan
-is to use `Python Eggs
-<http://peak.telecommunity.com/DevCenter/PythonEggs>`_, but it's just
-a plan. Python Eggs are still in development (though usable), and it
-requires creating packages for each project (which is feasible, but
-requires a fair amount of grunt work).
-
diff --git a/setup.cfg b/setup.cfg
index 2dea101..73e9f58 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,13 +3,20 @@ tag_build = dev
tag_svn_revision = true
[pudge]
-docs = docs/index.txt
+theme = lesscode.org
+docs = docs/index.txt docs/DeveloperGuidelines.txt docs/StyleGuide.txt
+ docs/testing-applications.txt docs/url-parsing-with-wsgi.txt
dest = docs/html
modules = paste
-title = Python Paste
+title = Paste Core
+organization = Python Paste
+organization_url = http://pythonpaste.org/
+trac_url = http://pythonpaste.org/trac/
+blog_url = http://pythonpaste.org/news/
+mailing_list_url = http://pythonpaste.org/community/mailing-list.html
[publish]
doc-dir=docs/html
-doc-dest=scp://ianb@webwareforpython.org/home/paste/htdocs/paste-test
+doc-dest=scp://ianb@webwareforpython.org/home/paste/htdocs/new/paste
make-dirs=1