Remove generated HTML docs from the version control system.
This commit is contained in:
parent
b799be7d7f
commit
5a2856c6d5
|
@ -1,6 +1,5 @@
|
|||
global-include *.py *.ptl
|
||||
include *.txt MANIFEST.in TODO
|
||||
include doc/*.txt doc/*.css doc/Makefile
|
||||
recursive-include doc *.html
|
||||
include doc/*.txt doc/*.css doc/*.html doc/Makefile
|
||||
include demo/*.cgi demo/*.conf demo/*.sh
|
||||
include src/*.c src/Makefile
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document">
|
||||
<div class="section" id="installing-quixote">
|
||||
<h1><a name="installing-quixote">Installing Quixote</a></h1>
|
||||
<p>Quixote requires Python 2.3 or later.</p>
|
||||
<p>If you have a previously installed quixote, we strongly recommend that
|
||||
you remove it before installing a new one.
|
||||
First, find out where your old Quixote installation is:</p>
|
||||
<blockquote>
|
||||
python -c "import os, quixote; print os.path.dirname(quixote.__file__)"</blockquote>
|
||||
<p>and then remove away the reported directory. (If the import fails, then
|
||||
you don't have an existing Quixote installation.)</p>
|
||||
<p>Now install the new version by running (in the distribution directory),</p>
|
||||
<blockquote>
|
||||
python setup.py install</blockquote>
|
||||
<p>and you're done.</p>
|
||||
</div>
|
||||
<div class="section" id="quick-start">
|
||||
<h1><a name="quick-start">Quick start</a></h1>
|
||||
<p>In a terminal window, run server/simple_server.py.
|
||||
In a browser, open <a class="reference" href="http://localhost:8080">http://localhost:8080</a></p>
|
||||
</div>
|
||||
<div class="section" id="upgrading-a-quixote-1-application-to-quixote-2">
|
||||
<h1><a name="upgrading-a-quixote-1-application-to-quixote-2">Upgrading a Quixote 1 application to Quixote 2.</a></h1>
|
||||
<p>See upgrading.txt for details.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
255
doc/PTL.html
255
doc/PTL.html
|
@ -1,255 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>PTL: Python Template Language</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="ptl-python-template-language">
|
||||
<h1 class="title">PTL: Python Template Language</h1>
|
||||
<div class="section" id="introduction">
|
||||
<h1><a name="introduction">Introduction</a></h1>
|
||||
<p>PTL is the templating language used by Quixote. Most web templating
|
||||
languages embed a real programming language in HTML, but PTL inverts
|
||||
this model by merely tweaking Python to make it easier to generate
|
||||
HTML pages (or other forms of text). In other words, PTL is basically
|
||||
Python with a novel way to specify function return values.</p>
|
||||
<p>Specifically, a PTL template is designated by inserting a <tt class="literal"><span class="pre">[plain]</span></tt>
|
||||
or <tt class="literal"><span class="pre">[html]</span></tt> modifier after the function name. The value of
|
||||
expressions inside templates are kept, not discarded. If the type is
|
||||
<tt class="literal"><span class="pre">[html]</span></tt> then non-literal strings are passed through a function that
|
||||
escapes HTML special characters.</p>
|
||||
</div>
|
||||
<div class="section" id="plain-text-templates">
|
||||
<h1><a name="plain-text-templates">Plain text templates</a></h1>
|
||||
<p>Here's a sample plain text template:</p>
|
||||
<pre class="literal-block">
|
||||
def foo [plain] (x, y = 5):
|
||||
"This is a chunk of static text."
|
||||
greeting = "hello world" # statement, no PTL output
|
||||
print 'Input values:', x, y
|
||||
z = x + y
|
||||
"""You can plug in variables like x (%s)
|
||||
in a variety of ways.""" % x
|
||||
|
||||
"\n\n"
|
||||
"Whitespace is important in generated text.\n"
|
||||
"z = "; z
|
||||
", but y is "
|
||||
y
|
||||
"."
|
||||
</pre>
|
||||
<p>Obviously, templates can't have docstrings, but otherwise they follow
|
||||
Python's syntactic rules: indentation indicates scoping, single-quoted
|
||||
and triple-quoted strings can be used, the same rules for continuing
|
||||
lines apply, and so forth. PTL also follows all the expected semantics
|
||||
of normal Python code: so templates can have parameters, and the
|
||||
parameters can have default values, be treated as keyword arguments,
|
||||
etc.</p>
|
||||
<p>The difference between a template and a regular Python function is that
|
||||
inside a template the result of expressions are saved as the return
|
||||
value of that template. Look at the first part of the example again:</p>
|
||||
<pre class="literal-block">
|
||||
def foo [plain] (x, y = 5):
|
||||
"This is a chunk of static text."
|
||||
greeting = "hello world" # statement, no PTL output
|
||||
print 'Input values:', x, y
|
||||
z = x + y
|
||||
"""You can plug in variables like x (%s)
|
||||
in a variety of ways.""" % x
|
||||
</pre>
|
||||
<p>Calling this template with <tt class="literal"><span class="pre">foo(1,</span> <span class="pre">2)</span></tt> results in the following
|
||||
string:</p>
|
||||
<pre class="literal-block">
|
||||
This is a chunk of static text.You can plug in variables like x (1)
|
||||
in a variety of ways.
|
||||
</pre>
|
||||
<p>Normally when Python evaluates expressions inside functions, it just
|
||||
discards their values, but in a <tt class="literal"><span class="pre">[plain]</span></tt> PTL template the value is
|
||||
converted to a string using <tt class="literal"><span class="pre">str()</span></tt> and appended to the template's
|
||||
return value. There's a single exception to this rule: <tt class="literal"><span class="pre">None</span></tt> is the
|
||||
only value that's ever ignored, adding nothing to the output. (If this
|
||||
weren't the case, calling methods or functions that return <tt class="literal"><span class="pre">None</span></tt>
|
||||
would require assigning their value to a variable. You'd have to write
|
||||
<tt class="literal"><span class="pre">dummy</span> <span class="pre">=</span> <span class="pre">list.sort()</span></tt> in PTL code, which would be strange and
|
||||
confusing.)</p>
|
||||
<p>The initial string in a template isn't treated as a docstring, but is
|
||||
just incorporated in the generated output; therefore, templates can't
|
||||
have docstrings. No whitespace is ever automatically added to the
|
||||
output, resulting in <tt class="literal"><span class="pre">...text.You</span> <span class="pre">can</span> <span class="pre">...</span></tt> from the example. You'd
|
||||
have to add an extra space to one of the string literals to correct
|
||||
this.</p>
|
||||
<p>The assignment to the <tt class="literal"><span class="pre">greeting</span></tt> local variable is a statement, not an
|
||||
expression, so it doesn't return a value and produces no output. The
|
||||
output from the <tt class="literal"><span class="pre">print</span></tt> statement will be printed as usual, but won't
|
||||
go into the string generated by the template. Quixote directs standard
|
||||
output into Quixote's debugging log; if you're using PTL on its own, you
|
||||
should consider doing something similar. <tt class="literal"><span class="pre">print</span></tt> should never be used
|
||||
to generate output returned to the browser, only for adding debugging
|
||||
traces to a template.</p>
|
||||
<p>Inside templates, you can use all of Python's control-flow statements:</p>
|
||||
<pre class="literal-block">
|
||||
def numbers [plain] (n):
|
||||
for i in range(n):
|
||||
i
|
||||
" " # PTL does not add any whitespace
|
||||
</pre>
|
||||
<p>Calling <tt class="literal"><span class="pre">numbers(5)</span></tt> will return the string <tt class="literal"><span class="pre">"1</span> <span class="pre">2</span> <span class="pre">3</span> <span class="pre">4</span> <span class="pre">5</span> <span class="pre">"</span></tt>. You can
|
||||
also have conditional logic or exception blocks:</p>
|
||||
<pre class="literal-block">
|
||||
def international_hello [plain] (language):
|
||||
if language == "english":
|
||||
"hello"
|
||||
elif language == "french":
|
||||
"bonjour"
|
||||
else:
|
||||
raise ValueError, "I don't speak %s" % language
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="html-templates">
|
||||
<h1><a name="html-templates">HTML templates</a></h1>
|
||||
<p>Since PTL is usually used to generate HTML documents, an <tt class="literal"><span class="pre">[html]</span></tt>
|
||||
template type has been provided to make generating HTML easier.</p>
|
||||
<p>A common error when generating HTML is to grab data from the browser
|
||||
or from a database and incorporate the contents without escaping
|
||||
special characters such as '<' and '&'. This leads to a class of
|
||||
security bugs called "cross-site scripting" bugs, where a hostile user
|
||||
can insert arbitrary HTML in your site's output that can link to other
|
||||
sites or contain JavaScript code that does something nasty (say,
|
||||
popping up 10,000 browser windows).</p>
|
||||
<p>Such bugs occur because it's easy to forget to HTML-escape a string,
|
||||
and forgetting it in just one location is enough to open a hole. PTL
|
||||
offers a solution to this problem by being able to escape strings
|
||||
automatically when generating HTML output, at the cost of slightly
|
||||
diminished performance (a few percent).</p>
|
||||
<p>Here's how this feature works. PTL defines a class called
|
||||
<tt class="literal"><span class="pre">htmltext</span></tt> that represents a string that's already been HTML-escaped
|
||||
and can be safely sent to the client. The function <tt class="literal"><span class="pre">htmlescape(string)</span></tt>
|
||||
is used to escape data, and it always returns an <tt class="literal"><span class="pre">htmltext</span></tt>
|
||||
instance. It does nothing if the argument is already <tt class="literal"><span class="pre">htmltext</span></tt>.</p>
|
||||
<p>If a template function is declared <tt class="literal"><span class="pre">[html]</span></tt> instead of <tt class="literal"><span class="pre">[text]</span></tt>
|
||||
then two things happen. First, all literal strings in the function
|
||||
become instances of <tt class="literal"><span class="pre">htmltext</span></tt> instead of Python's <tt class="literal"><span class="pre">str</span></tt>. Second,
|
||||
the values of expressions are passed through <tt class="literal"><span class="pre">htmlescape()</span></tt> instead
|
||||
of <tt class="literal"><span class="pre">str()</span></tt>.</p>
|
||||
<p><tt class="literal"><span class="pre">htmltext</span></tt> type is like the <tt class="literal"><span class="pre">str</span></tt> type except that operations
|
||||
combining strings and <tt class="literal"><span class="pre">htmltext</span></tt> instances will result in the string
|
||||
being passed through <tt class="literal"><span class="pre">htmlescape()</span></tt>. For example:</p>
|
||||
<pre class="literal-block">
|
||||
>>> from quixote.html import htmltext
|
||||
>>> htmltext('a') + 'b'
|
||||
<htmltext 'ab'>
|
||||
>>> 'a' + htmltext('b')
|
||||
<htmltext 'ab'>
|
||||
>>> htmltext('a%s') % 'b'
|
||||
<htmltext 'ab'>
|
||||
>>> response = 'green eggs & ham'
|
||||
>>> htmltext('The response was: %s') % response
|
||||
<htmltext 'The response was: green eggs &amp; ham'>
|
||||
</pre>
|
||||
<p>Note that calling <tt class="literal"><span class="pre">str()</span></tt> strips the <tt class="literal"><span class="pre">htmltext</span></tt> type and should be
|
||||
avoided since it usually results in characters being escaped more than
|
||||
once. While <tt class="literal"><span class="pre">htmltext</span></tt> behaves much like a regular string, it is
|
||||
sometimes necessary to insert a <tt class="literal"><span class="pre">str()</span></tt> inside a template in order
|
||||
to obtain a genuine string. For example, the <tt class="literal"><span class="pre">re</span></tt> module requires
|
||||
genuine strings. We have found that explicit calls to <tt class="literal"><span class="pre">str()</span></tt> can
|
||||
often be avoided by splitting some code out of the template into a
|
||||
helper function written in regular Python.</p>
|
||||
<p>It is also recommended that the <tt class="literal"><span class="pre">htmltext</span></tt> constructor be used as
|
||||
sparingly as possible. The reason is that when using the htmltext
|
||||
feature of PTL, explicit calls to <tt class="literal"><span class="pre">htmltext</span></tt> become the most likely
|
||||
source of cross-site scripting holes. Calling <tt class="literal"><span class="pre">htmltext</span></tt> is like
|
||||
saying "I am absolutely sure this piece of data cannot contain malicious
|
||||
HTML code injected by a user. Don't escape HTML special characters
|
||||
because I want them."</p>
|
||||
<p>Note that literal strings in template functions declared with
|
||||
<tt class="literal"><span class="pre">[html]</span></tt> are htmltext instances, and therefore won't be escaped.
|
||||
You'll only need to use <tt class="literal"><span class="pre">htmltext</span></tt> when HTML markup comes from
|
||||
outside the template. For example, if you want to include a file
|
||||
containing HTML:</p>
|
||||
<pre class="literal-block">
|
||||
def output_file [html] ():
|
||||
'<html><body>' # does not get escaped
|
||||
htmltext(open("myfile.html").read())
|
||||
'</body></html>'
|
||||
</pre>
|
||||
<p>In the common case, templates won't be dealing with HTML markup from
|
||||
external sources, so you can write straightforward code. Consider
|
||||
this function to generate the contents of the <tt class="literal"><span class="pre">HEAD</span></tt> element:</p>
|
||||
<pre class="literal-block">
|
||||
def meta_tags [html] (title, description):
|
||||
'<title>%s</title>' % title
|
||||
'<meta name="description" content="%s">\n' % description
|
||||
</pre>
|
||||
<p>There are no calls to <tt class="literal"><span class="pre">htmlescape()</span></tt> at all, but string literals
|
||||
such as <tt class="literal"><span class="pre"><title>%s</title></span></tt> have all be turned into <tt class="literal"><span class="pre">htmltext</span></tt>
|
||||
instances, so the string variables will be automatically escaped:</p>
|
||||
<pre class="literal-block">
|
||||
>>> t.meta_tags('Catalog', 'A catalog of our cool products')
|
||||
<htmltext '<title>Catalog</title>
|
||||
<meta name="description" content="A catalog of our cool products">\n'>
|
||||
>>> t.meta_tags('Dissertation on <HEAD>',
|
||||
... 'Discusses the "LINK" and "META" tags')
|
||||
<htmltext '<title>Dissertation on &lt;HEAD&gt;</title>
|
||||
<meta name="description"
|
||||
content="Discusses the &quot;LINK&quot; and &quot;META&quot; tags">\n'>
|
||||
>>>
|
||||
</pre>
|
||||
<p>Note how the title and description have had HTML-escaping applied to them.
|
||||
(The output has been manually pretty-printed to be more readable.)</p>
|
||||
<p>Once you start using <tt class="literal"><span class="pre">htmltext</span></tt> in one of your templates, mixing
|
||||
plain and HTML templates is tricky because of <tt class="literal"><span class="pre">htmltext</span></tt>'s automatic
|
||||
escaping; plain templates that generate HTML tags will be
|
||||
double-escaped. One approach is to just use HTML templates throughout
|
||||
your application. Alternatively you can use <tt class="literal"><span class="pre">str()</span></tt> to convert
|
||||
<tt class="literal"><span class="pre">htmltext</span></tt> instances to regular Python strings; just be sure the
|
||||
resulting string isn't HTML-escaped again.</p>
|
||||
<p>Two implementations of <tt class="literal"><span class="pre">htmltext</span></tt> are provided, one written in pure
|
||||
Python and a second one implemented as a C extension. Both versions
|
||||
have seen production use.</p>
|
||||
</div>
|
||||
<div class="section" id="ptl-modules">
|
||||
<h1><a name="ptl-modules">PTL modules</a></h1>
|
||||
<p>PTL templates are kept in files with the extension .ptl. Like Python
|
||||
files, they are byte-compiled on import, and the byte-code is written to
|
||||
a compiled file with the extension <tt class="literal"><span class="pre">.pyc</span></tt>. Since vanilla Python
|
||||
doesn't know anything about PTL, Quixote provides an import hook to let
|
||||
you import PTL files just like regular Python modules. The standard way
|
||||
to install this import hook is by calling the <tt class="literal"><span class="pre">enable_ptl()</span></tt> function:</p>
|
||||
<pre class="literal-block">
|
||||
from quixote import enable_ptl
|
||||
enable_ptl()
|
||||
</pre>
|
||||
<p>(Note: if you're using ZODB, always import ZODB <em>before</em> installing the
|
||||
PTL import hook. There's some interaction which causes importing the
|
||||
TimeStamp module to fail when the PTL import hook is installed; we
|
||||
haven't debugged the problem. A similar problem has been reported for
|
||||
BioPython and win32com.client imports.)</p>
|
||||
<p>Once the import hook is installed, PTL files can be imported as if they
|
||||
were Python modules. If all the example templates shown here were put
|
||||
into a file named <tt class="literal"><span class="pre">foo.ptl</span></tt>, you could then write Python code that did
|
||||
this:</p>
|
||||
<pre class="literal-block">
|
||||
from foo import numbers
|
||||
def f():
|
||||
return numbers(10)
|
||||
</pre>
|
||||
<p>You may want to keep this little function in your <tt class="literal"><span class="pre">PYTHONSTARTUP</span></tt>
|
||||
file:</p>
|
||||
<pre class="literal-block">
|
||||
def ptl():
|
||||
try:
|
||||
import ZODB
|
||||
except ImportError:
|
||||
pass
|
||||
from quixote import enable_ptl
|
||||
enable_ptl()
|
||||
</pre>
|
||||
<p>This is useful if you want to interactively play with a PTL module.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
206
doc/demo.html
206
doc/demo.html
|
@ -1,206 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Running the Quixote Demos</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="running-the-quixote-demos">
|
||||
<h1 class="title">Running the Quixote Demos</h1>
|
||||
<p>Quixote comes with some demonstration applications in the demo directory.
|
||||
After quixote is installed (see INSTALL.txt for instructions),
|
||||
you can run the demos using the scripts located in the server directory.</p>
|
||||
<p>Each server script is written for a specific method of connecting a
|
||||
quixote publisher to a web server, and you will ultimately want to
|
||||
choose the one that matches your needs. More information about the
|
||||
different server scripts may be found in the scripts themselves and in
|
||||
web-server.txt. To start, though, the easiest way to view the demos
|
||||
is as follows: in a terminal window, run server/simple_server.py, and
|
||||
in a browser, open <a class="reference" href="http://localhost:8080">http://localhost:8080</a>.</p>
|
||||
<p>The simple_server.py script prints a usage message if you run it with
|
||||
a '--help' command line argument. You can run different demos by
|
||||
using the '--factory' option to identify a callable that creates the
|
||||
publisher you want to use. In particular, you might try these demos:</p>
|
||||
<blockquote>
|
||||
simple_server.py --factory quixote.demo.mini_demo.create_publisher</blockquote>
|
||||
<p>or</p>
|
||||
<blockquote>
|
||||
simple_server.py --factory quixote.demo.altdemo.create_publisher</blockquote>
|
||||
<div class="section" id="understanding-the-mini-demo">
|
||||
<h1><a name="understanding-the-mini-demo">Understanding the mini_demo</a></h1>
|
||||
<dl>
|
||||
<dt>Start the mini demo by running the command:</dt>
|
||||
<dd>simple_server.py --factory quixote.demo.mini_demo.create_publisher</dd>
|
||||
</dl>
|
||||
<p>In a browser, load <a class="reference" href="http://localhost:8080">http://localhost:8080</a>. In your browser, you should
|
||||
see "Welcome ..." page. In your terminal window, you will see a
|
||||
"localhost - - ..." line for each request. These are access log
|
||||
messages from the web server.</p>
|
||||
<p>Look at the source code in demo/mini_demo.py. Near the bottom you
|
||||
will find the create_publisher() function. The create_publisher()
|
||||
function creates a Publisher instance whose root directory is an
|
||||
instance of the RootDirectory class defined just above. When a
|
||||
request arrives, the Publisher calls the _q_traverse() method on the
|
||||
root directory. In this case, the RootDirectory is using the standard
|
||||
_q_traverse() implementation, inherited from Directory.</p>
|
||||
<p>Look, preferably in another window, at the source code for
|
||||
_q_traverse() in directory.py. The path argument provided to
|
||||
_q_traverse() is a list of string components of the path part of the
|
||||
URL, obtained by splitting the request location at each '/' and
|
||||
dropping the first element (which is always '') For example, if the
|
||||
path part of the URL is '/', the path argument to _q_traverse() is
|
||||
['']. If the path part of the URL is '/a', the path argument to
|
||||
_q_traverse() is ['a']. If the path part of the URL is '/a/', the
|
||||
path argument to _q_traverse() is ['a', ''].</p>
|
||||
<p>Looking at the code of _q_traverse(), observe that it starts by
|
||||
splitting off the first component of the path and calling
|
||||
_q_translate() to see if there is a designated attribute name
|
||||
corresponding to this component. For the '/' page, the component is
|
||||
'', and _q_translate() returns the attribute name '_q_index'. The
|
||||
_q_traverse() function goes on to lookup the _q_index method and
|
||||
return the result of calling it.</p>
|
||||
<p>Looking back at mini_demo.py, you can see that the RootDirectory class
|
||||
includes a _q_index() method, and this method does return the HTML for
|
||||
<a class="reference" href="http://localhost:8080/">http://localhost:8080/</a></p>
|
||||
<p>As mentioned above, the _q_translate() identifies a "designated"
|
||||
attribute name for a given component. The default implementation uses
|
||||
self._q_exports to define this designation. In particular, if the
|
||||
component is in self._q_exports, then it is returned as the attribute
|
||||
name, except in the special case of '', which is translated to the
|
||||
special attribute name '_q_index'.</p>
|
||||
<p>When you click on the link on the top page, you get
|
||||
<a class="reference" href="http://localhost:8080/hello">http://localhost:8080/hello</a>. In this case, the path argument to the
|
||||
_q_traverse() call is ['hello'], and the return value is the result of
|
||||
calling the hello() method.</p>
|
||||
<p>Feeling bold? (Just kidding, this won't hurt at all.) Try opening
|
||||
<a class="reference" href="http://localhost:8080/bogus">http://localhost:8080/bogus</a>. This is what happens when _q_traverse()
|
||||
raises a TraversalError. A TraversalError is no big deal, but how
|
||||
does quixote handle more exceptional exceptions? To see, you can
|
||||
introduce one by editing mini_demo.py. Try inserting the line "raise
|
||||
'ouch'" into the hello() method. Kill the demo server (Control-c) and
|
||||
start a new one with the same command as before. Now load the
|
||||
<a class="reference" href="http://localhost:8080/hello">http://localhost:8080/hello</a> page. You should see a plain text python
|
||||
traceback followed by some information extracted from the HTTP
|
||||
request. This information is always printed to the error log on an
|
||||
exception. Here, it is also displayed in the browser because the
|
||||
create_publisher() function made a publisher using the 'plain' value
|
||||
for the display_exceptions keyword argument. If you omit that keyword
|
||||
argument from the Publisher constructor, the browser will get an
|
||||
"Internal Server Error" message instead of the full traceback. If you
|
||||
provide the value 'html', the browser displays a prettier version of
|
||||
the traceback.</p>
|
||||
<p>One more thing to try here. Replace your 'raise "ouch"' line in the hello() method with 'print "ouch"'. If you restart the server and load the /hello page,
|
||||
you will see that print statements go the the error log (in this case, your
|
||||
terminal window). This can be useful.</p>
|
||||
</div>
|
||||
<div class="section" id="understanding-the-root-demo">
|
||||
<h1><a name="understanding-the-root-demo">Understanding the root demo</a></h1>
|
||||
<dl>
|
||||
<dt>Start the root demo by running the command:</dt>
|
||||
<dd>simple_server.py --factory quixote.demo.create_publisher</dd>
|
||||
</dl>
|
||||
<p>In a browser, open <a class="reference" href="http://localhost:8080">http://localhost:8080</a> as before.
|
||||
Click around at will.</p>
|
||||
<p>This is the default demo, but it is more complicated than the
|
||||
mini_demo described above. The create_publisher() function in
|
||||
quixote.demo.__init__.py creates a publisher whose root directory is
|
||||
an instance of quixote.demo.root.RootDirectory. Note that the source
|
||||
code is a file named "root.ptl". The suffix of "ptl" indicates that
|
||||
it is a PTL file, and the import must follow a call to
|
||||
quixote.enable_ptl() or else the source file will not be found or
|
||||
compiled. The quixote.demo.__init__.py file takes care of that.</p>
|
||||
<p>Take a look at the source code in root.ptl. You will see code that
|
||||
looks like regular python, except that some function definitions have
|
||||
"[html]" between the function name and the parameter list. These
|
||||
functions are ptl templates. For details about PTL, see the PTL.txt
|
||||
file.</p>
|
||||
<p>This RootDirectory class is similar to the one in mini_demo.py, in
|
||||
that it has a _q_index() method and '' appears in the _q_exports list.
|
||||
One new feature here is the presence of a tuple in the _q_exports
|
||||
list. Most of the time, the elements of the _q_exports lists are just
|
||||
strings that name attributes that should be available as URL
|
||||
components. This pattern does not work, however, when the particular
|
||||
URL component you want to use includes characters (like '.') that
|
||||
can't appear in Python attribute names. To work around these cases,
|
||||
the _q_exports list may contain tuples such as ("favicon.ico",
|
||||
"favicon_ico") to designate "favicon_ico" as the attribute name
|
||||
corresponding the the "favicon.ico" URL component.</p>
|
||||
<p>Looking at the RootDirectoryMethods, including plain(), css() and
|
||||
favon_ico(), you will see examples where, in addition to returning a
|
||||
string containing the body of the HTTP response, the function also
|
||||
makes side-effect modifications to the response object itself, to set
|
||||
the content type and the expiration time for the response.
|
||||
Most of the time, these direct modifications to the response are
|
||||
not needed. When they are, though, the get_response() function
|
||||
gives you direct access to the response instance.</p>
|
||||
<p>The RootDirectory here also sets an 'extras' attribute to be an
|
||||
instance of ExtraDirectory, imported from the quixote.demo.extras
|
||||
module. Note that 'extras' also appears in the _q_exports list. This
|
||||
is the ordinary way to extend your URL space through another '/'.
|
||||
For example, the URL path '/extras/' will result in a call to
|
||||
the ExtraDirectory instance's _q_index() method.</p>
|
||||
</div>
|
||||
<div class="section" id="the-q-lookup-method">
|
||||
<h1><a name="the-q-lookup-method">The _q_lookup() method</a></h1>
|
||||
<p>Now take a look at the ExtraDirectory class in extras.ptl. This class
|
||||
exhibits some more advanced publishing features. If you look back at
|
||||
the default _q_traverse() implementation (in directory.py), you will
|
||||
see that the _q_traverse does not give up if _q_translate() returns
|
||||
None, indicating that the path component has no designated
|
||||
corresponding attribute name. In this case, _q_traverse() tries
|
||||
calling self._q_lookup() to see if the object of interest can be found
|
||||
in a different way. Note that _q_lookup() takes the component as an
|
||||
argument and must return either (if there is more path to traverse) a
|
||||
Directory instance, or else (if the component is the last in the path)
|
||||
a callable or a string.</p>
|
||||
<p>In this particular case, the ExtrasDirectory._q_lookup() call returns
|
||||
an instance of IntegerUI (a subclass of Directory). The interest
|
||||
here, unlike the ExtrasDirectory() instance itself, is created
|
||||
on-the-fly during the traversal, especially for this particular
|
||||
component. Try loading <a class="reference" href="http://localhost:8080/extras/12/">http://localhost:8080/extras/12/</a> to see how
|
||||
this behaves.</p>
|
||||
<p>Note that the correct URL to get to the IntegerUI(12)._q_index() call
|
||||
ends with a '/'. This can sometimes be confusing to people who expect
|
||||
<a class="reference" href="http://localhost:8080/extras/12">http://localhost:8080/extras/12</a> to yield the same page as
|
||||
<a class="reference" href="http://localhost:8080/extras/12/">http://localhost:8080/extras/12/</a>. If given the path ['extras', '12'],
|
||||
the default _q_traverse() ends up <em>calling</em> the instance of IntegerUI.
|
||||
The Directory.__call__() (see directory.py) determines the result: if
|
||||
no form values were submitted and adding a slash would produce a page,
|
||||
the call returns the result of calling quixote.redirect(). The
|
||||
redirect() call here causes the server to issue a permanent redirect
|
||||
response to the path with the slash added. When this automatic
|
||||
redirect is used, a message is printed to the error log. If the
|
||||
conditions for a redirect are not met, the call falls back to raising
|
||||
a TraversalError. [Note, if you don't like this redirect behavior,
|
||||
override, replace, or delete Directory.__call__]</p>
|
||||
<p>The _q_lookup() pattern is useful when you want to allow URL
|
||||
components that you either don't know or don't want to list in
|
||||
_q_exports ahead of time.</p>
|
||||
</div>
|
||||
<div class="section" id="the-q-resolve-method">
|
||||
<h1><a name="the-q-resolve-method">The _q_resolve() method</a></h1>
|
||||
<p>Note that the ExtraDirectory class inherits from Resolving (in
|
||||
addition to Directory). The Resolving mixin modifies the
|
||||
_q_traverse() so that, when a component has an attribute name
|
||||
designated by _q_translate(), but the Directory instance does not
|
||||
actually <em>have</em> that attribute, the _q_resolve() method is called to
|
||||
"resolve" the trouble. Typically, the _q_resolve() imports or
|
||||
constructs what <em>should</em> be the value of the designated attribute.
|
||||
The modified _q_translate() sets the attribute value so that the
|
||||
_q_resolve() won't be called again for the same attribute. The
|
||||
_q_resolve() pattern is useful when you want to delay the work of
|
||||
constructing the values for exported attributes.</p>
|
||||
</div>
|
||||
<div class="section" id="forms">
|
||||
<h1><a name="forms">Forms</a></h1>
|
||||
<p>You can't get very far writing web applications without writing forms.
|
||||
The root demo includes, at <a class="reference" href="http://localhost:8080/extras/form">http://localhost:8080/extras/form</a>, a page
|
||||
that demonstrates basic usage of the Form class and widgets defined in
|
||||
the quixote.form package.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,377 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Converting form1 forms to use the form2 library</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="converting-form1-forms-to-use-the-form2-library">
|
||||
<h1 class="title">Converting form1 forms to use the form2 library</h1>
|
||||
<div class="section" id="note">
|
||||
<h1><a name="note">Note:</a></h1>
|
||||
<p>The packages names have changed in Quixote 2.</p>
|
||||
<p>Quixote form1 forms are now in the package quixote.form1.
|
||||
(In Quixote 1, they were in quixote.form.)</p>
|
||||
<p>Quixote form2 forms are now in the package quixote.form.
|
||||
(In Quixote 1, they were in quixote.form2.)</p>
|
||||
</div>
|
||||
<div class="section" id="introduction">
|
||||
<h1><a name="introduction">Introduction</a></h1>
|
||||
<p>These are some notes and examples for converting Quixote form1 forms,
|
||||
that is forms derived from <tt class="literal"><span class="pre">quixote.form1.Form</span></tt>, to the newer form2
|
||||
forms.</p>
|
||||
<p>Form2 forms are more flexible than their form1 counterparts in that they
|
||||
do not require you to use the <tt class="literal"><span class="pre">Form</span></tt> class as a base to get form
|
||||
functionality as form1 forms did. Form2 forms can be instantiated
|
||||
directly and then manipulated as instances. You may also continue to
|
||||
use inheritance for your form2 classes to get form functionality,
|
||||
particularly if the structured separation of <tt class="literal"><span class="pre">process</span></tt>, <tt class="literal"><span class="pre">render</span></tt>,
|
||||
and <tt class="literal"><span class="pre">action</span></tt> is desirable.</p>
|
||||
<p>There are many ways to get from form1 code ported to form2. At one
|
||||
end of the spectrum is to rewrite the form class using a functional
|
||||
programing style. This method is arguably best since the functional
|
||||
style makes the flow of control clearer.</p>
|
||||
<p>The other end of the spectrum and normally the easiest way to port
|
||||
form1 forms to form2 is to use the <tt class="literal"><span class="pre">compatibility</span></tt> module provided
|
||||
in the form2 package. The compatibility module's Form class provides
|
||||
much of the same highly structured machinery (via a <tt class="literal"><span class="pre">handle</span></tt> master
|
||||
method) that the form1 framework uses.</p>
|
||||
</div>
|
||||
<div class="section" id="converting-form1-forms-using-using-the-compatibility-module">
|
||||
<h1><a name="converting-form1-forms-using-using-the-compatibility-module">Converting form1 forms using using the compatibility module</a></h1>
|
||||
<p>Here's the short list of things to do to convert form1 forms to
|
||||
form2 using compatibility.</p>
|
||||
<blockquote>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">Import the Form base class from <tt class="literal"><span class="pre">quixote.form.compatibility</span></tt>
|
||||
rather than from quixote.form1.</p>
|
||||
</li>
|
||||
<li><p class="first">Getting and setting errors is slightly different. In your form's
|
||||
process method, where errors are typically set, form2
|
||||
has a new interface for marking a widget as having an error.</p>
|
||||
<blockquote>
|
||||
<p>Form1 API:</p>
|
||||
<pre class="literal-block">
|
||||
self.error['widget_name'] = 'the error message'
|
||||
</pre>
|
||||
<p>Form2 API:</p>
|
||||
<pre class="literal-block">
|
||||
self.set_error('widget_name', 'the error message')
|
||||
</pre>
|
||||
</blockquote>
|
||||
<p>If you want to find out if the form already has errors, change
|
||||
the form1 style of direct references to the <tt class="literal"><span class="pre">self.errors</span></tt>
|
||||
dictionary to a call to the <tt class="literal"><span class="pre">has_errors</span></tt> method.</p>
|
||||
<blockquote>
|
||||
<p>Form1 API:</p>
|
||||
<pre class="literal-block">
|
||||
if not self.error:
|
||||
do some more error checking...
|
||||
</pre>
|
||||
<p>Form2 API:</p>
|
||||
<pre class="literal-block">
|
||||
if not self.has_errors():
|
||||
do some more error checking...
|
||||
</pre>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li><p class="first">Form2 select widgets no longer take <tt class="literal"><span class="pre">allowed_values</span></tt> or
|
||||
<tt class="literal"><span class="pre">descriptions</span></tt> arguments. If you are adding type of form2 select
|
||||
widget, you must provide the <tt class="literal"><span class="pre">options</span></tt> argument instead. Options
|
||||
are the way you define the list of things that are selectable and
|
||||
what is returned when they are selected. the options list can be
|
||||
specified in in one of three ways:</p>
|
||||
<pre class="literal-block">
|
||||
options: [objects:any]
|
||||
or
|
||||
options: [(object:any, description:any)]
|
||||
or
|
||||
options: [(object:any, description:any, key:any)]
|
||||
</pre>
|
||||
<p>An easy way to construct options if you already have
|
||||
allowed_values and descriptions is to use the built-in function
|
||||
<tt class="literal"><span class="pre">zip</span></tt> to define options:</p>
|
||||
<pre class="literal-block">
|
||||
options=zip(allowed_values, descriptions)
|
||||
</pre>
|
||||
</li>
|
||||
</ol>
|
||||
<blockquote>
|
||||
Note, however, that often it is simpler to to construct the
|
||||
<tt class="literal"><span class="pre">options</span></tt> list directly.</blockquote>
|
||||
<ol class="arabic simple" start="4">
|
||||
<li>You almost certainly want to include some kind of cascading style
|
||||
sheet (since form2 forms render with minimal markup). There is a
|
||||
basic set of CSS rules in <tt class="literal"><span class="pre">quixote.form.css</span></tt>.</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<p>Here's the longer list of things you may need to tweak in order for
|
||||
form2 compatibility forms to work with your form1 code.</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li><p class="first"><tt class="literal"><span class="pre">widget_type</span></tt> widget class attribute is gone. This means when
|
||||
adding widgets other than widgets defined in <tt class="literal"><span class="pre">quixote.form.widget</span></tt>,
|
||||
you must import the widget class into your module and pass the
|
||||
widget class as the first argument to the <tt class="literal"><span class="pre">add_widget</span></tt> method
|
||||
rather than using the <tt class="literal"><span class="pre">widget_type</span></tt> string.</p>
|
||||
</li>
|
||||
<li><p class="first">The <tt class="literal"><span class="pre">action_url</span></tt> argument to the form's render method is now
|
||||
a keyword argument.</p>
|
||||
</li>
|
||||
<li><p class="first">If you use <tt class="literal"><span class="pre">OptionSelectWidget</span></tt>, there is no longer a
|
||||
<tt class="literal"><span class="pre">get_current_option</span></tt> method. You can get the current value
|
||||
in the normal way.</p>
|
||||
</li>
|
||||
<li><p class="first"><tt class="literal"><span class="pre">ListWidget</span></tt> has been renamed to <tt class="literal"><span class="pre">WidgetList</span></tt>.</p>
|
||||
</li>
|
||||
<li><p class="first">There is no longer a <tt class="literal"><span class="pre">CollapsibleListWidget</span></tt> class. If you need
|
||||
this functionality, consider writing a 'deletable composite widget'
|
||||
to wrap your <tt class="literal"><span class="pre">WidgetList</span></tt> widgets in it:</p>
|
||||
<pre class="literal-block">
|
||||
class DeletableWidget(CompositeWidget):
|
||||
|
||||
def __init__(self, name, value=None,
|
||||
element_type=StringWidget,
|
||||
element_kwargs={}, **kwargs):
|
||||
CompositeWidget.__init__(self, name, value=value, **kwargs)
|
||||
self.add(HiddenWidget, 'deleted', value='0')
|
||||
if self.get('deleted') != '1':
|
||||
self.add(element_type, 'element', value=value,
|
||||
**element_kwargs)
|
||||
self.add(SubmitWidget, 'delete', value='Delete')
|
||||
if self.get('delete'):
|
||||
self.get_widget('deleted').set_value('1')
|
||||
|
||||
def _parse(self, request):
|
||||
if self.get('deleted') == '1':
|
||||
self.value = None
|
||||
else:
|
||||
self.value = self.get('element')
|
||||
|
||||
def render(self):
|
||||
if self.get('deleted') == '1':
|
||||
return self.get_widget('deleted').render()
|
||||
else:
|
||||
return CompositeWidget.render(self)
|
||||
</pre>
|
||||
</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p>Congratulations, now that you've gotten your form1 forms working in form2,
|
||||
you may wish to simplify this code using some of the new features available
|
||||
in form2 forms. Here's a list of things you may wish to consider:</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li><p class="first">In your process method, you don't really need to get a <tt class="literal"><span class="pre">form_data</span></tt>
|
||||
dictionary by calling <tt class="literal"><span class="pre">Form.process</span></tt> to ensure your widgets are
|
||||
parsed. Instead, the parsed value of any widget is easy to obtain
|
||||
using the widget's <tt class="literal"><span class="pre">get_value</span></tt> method or the form's
|
||||
<tt class="literal"><span class="pre">__getitem__</span></tt> method. So, instead of:</p>
|
||||
<pre class="literal-block">
|
||||
form_data = Form.process(self, request)
|
||||
val = form_data['my_widget']
|
||||
</pre>
|
||||
<p>You can use:</p>
|
||||
<pre class="literal-block">
|
||||
val = self['my_widget']
|
||||
</pre>
|
||||
<p>If the widget may or may not be in the form, you can use <tt class="literal"><span class="pre">get</span></tt>:</p>
|
||||
<pre class="literal-block">
|
||||
val = self.get('my_widget')
|
||||
</pre>
|
||||
</li>
|
||||
<li><p class="first">It's normally not necessary to provide the <tt class="literal"><span class="pre">action_url</span></tt> argument
|
||||
to the form's <tt class="literal"><span class="pre">render</span></tt> method.</p>
|
||||
</li>
|
||||
<li><p class="first">You don't need to save references to your widgets in your form
|
||||
class. You may have a particular reason for wanting to do that,
|
||||
but any widget added to the form using <tt class="literal"><span class="pre">add</span></tt> (or <tt class="literal"><span class="pre">add_widget</span></tt> in
|
||||
the compatibility module) can be retrieved using the form's
|
||||
<tt class="literal"><span class="pre">get_widget</span></tt> method.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</div>
|
||||
<div class="section" id="converting-form1-forms-to-form2-by-functional-rewrite">
|
||||
<h1><a name="converting-form1-forms-to-form2-by-functional-rewrite">Converting form1 forms to form2 by functional rewrite</a></h1>
|
||||
<p>The best way to get started on a functional version of a form2 rewrite
|
||||
is to look at a trivial example form first written using the form1
|
||||
inheritance model followed by it's form2 functional equivalent.</p>
|
||||
<p>First the form1 form:</p>
|
||||
<pre class="literal-block">
|
||||
class MyForm1Form(Form):
|
||||
def __init__(self, request, obj):
|
||||
Form.__init__(self)
|
||||
|
||||
if obj is None:
|
||||
self.obj = Obj()
|
||||
self.add_submit_button('add', 'Add')
|
||||
else:
|
||||
self.obj = obj
|
||||
self.add_submit_button('update', 'Update')
|
||||
|
||||
self.add_cancel_button('Cancel', request.get_path(1) + '/')
|
||||
|
||||
self.add_widget('single_select', 'obj_type',
|
||||
title='Object Type',
|
||||
value=self.obj.get_type(),
|
||||
allowed_values=list(obj.VALID_TYPES),
|
||||
descriptions=['type1', 'type2', 'type3'])
|
||||
self.add_widget('float', 'cost',
|
||||
title='Cost',
|
||||
value=obj.get_cost())
|
||||
|
||||
def render [html] (self, request, action_url):
|
||||
title = 'Obj %s: Edit Object' % self.obj
|
||||
header(title)
|
||||
Form.render(self, request, action_url)
|
||||
footer(title)
|
||||
|
||||
def process(self, request):
|
||||
form_data = Form.process(self, request)
|
||||
|
||||
if not self.error:
|
||||
if form_data['cost'] is None:
|
||||
self.error['cost'] = 'A cost is required.'
|
||||
elif form_data['cost'] < 0:
|
||||
self.error['cost'] = 'The amount must be positive'
|
||||
return form_data
|
||||
|
||||
def action(self, request, submit, form_data):
|
||||
self.obj.set_type(form_data['obj_type'])
|
||||
self.obj.set_cost(form_data['cost'])
|
||||
if submit == 'add':
|
||||
db = get_database()
|
||||
db.add(self.obj)
|
||||
else:
|
||||
assert submit == 'update'
|
||||
return request.redirect(request.get_path(1) + '/')
|
||||
</pre>
|
||||
<p>Here's the same form using form2 where the function operates on a Form
|
||||
instance it keeps a reference to it as a local variable:</p>
|
||||
<pre class="literal-block">
|
||||
def obj_form(request, obj):
|
||||
form = Form() # quixote.form.Form
|
||||
if obj is None:
|
||||
obj = Obj()
|
||||
form.add_submit('add', 'Add')
|
||||
else:
|
||||
form.add_submit('update', 'Update')
|
||||
form.add_submit('cancel', 'Cancel')
|
||||
|
||||
form.add_single_select('obj_type',
|
||||
title='Object Type',
|
||||
value=obj.get_type(),
|
||||
options=zip(obj.VALID_TYPES,
|
||||
['type1', 'type2', 'type3']))
|
||||
form.add_float('cost',
|
||||
title='Cost',
|
||||
value=obj.get_cost(),
|
||||
required=1)
|
||||
|
||||
def render [html] ():
|
||||
title = 'Obj %s: Edit Object' % obj
|
||||
header(title)
|
||||
form.render()
|
||||
footer(title)
|
||||
|
||||
def process():
|
||||
if form['cost'] < 0:
|
||||
self.set_error('cost', 'The amount must be positive')
|
||||
|
||||
def action(submit):
|
||||
obj.set_type(form['obj_type'])
|
||||
obj.set_cost(form['cost'])
|
||||
if submit == 'add':
|
||||
db = get_database()
|
||||
db.add(self.obj)
|
||||
else:
|
||||
assert submit == 'update'
|
||||
|
||||
exit_path = request.get_path(1) + '/'
|
||||
submit = form.get_submit()
|
||||
if submit == 'cancel':
|
||||
return request.redirect(exit_path)
|
||||
|
||||
if not form.is_submitted() or form.has_errors():
|
||||
return render()
|
||||
process()
|
||||
if form.has_errors():
|
||||
return render()
|
||||
|
||||
action(submit)
|
||||
return request.redirect(exit_path)
|
||||
</pre>
|
||||
<p>As you can see in the example, the function still has all of the same
|
||||
parts of it's form1 equivalent.</p>
|
||||
<blockquote>
|
||||
<ol class="arabic simple">
|
||||
<li>It determines if it's to create a new object or edit an existing one</li>
|
||||
<li>It adds submit buttons and widgets</li>
|
||||
<li>It has a function that knows how to render the form</li>
|
||||
<li>It has a function that knows how to do error processing on the form</li>
|
||||
<li>It has a function that knows how to register permanent changes to
|
||||
objects when the form is submitted successfully.</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<p>In the form2 example, we have used inner functions to separate out these
|
||||
parts. This, of course, is optional, but it does help readability once
|
||||
the form gets more complicated and has the additional advantage of
|
||||
mapping directly with it's form1 counterparts.</p>
|
||||
<p>Form2 functional forms do not have the <tt class="literal"><span class="pre">handle</span></tt> master-method that
|
||||
is called after the form is initialized. Instead, we deal with this
|
||||
functionality manually. Here are some things that the <tt class="literal"><span class="pre">handle</span></tt>
|
||||
portion of your form might need to implement illustrated in the
|
||||
order that often makes sense.</p>
|
||||
<blockquote>
|
||||
<ol class="arabic simple">
|
||||
<li>Get the value of any submit buttons using <tt class="literal"><span class="pre">form.get_submit</span></tt></li>
|
||||
<li>If the form has not been submitted yet, return <tt class="literal"><span class="pre">render()</span></tt>.</li>
|
||||
<li>See if the cancel button was pressed, if so return a redirect.</li>
|
||||
<li>Call your <tt class="literal"><span class="pre">process</span></tt> inner function to do any widget-level error
|
||||
checks. The form may already have set some errors, so you
|
||||
may wish to check for that before trying additional error checks.</li>
|
||||
<li>See if the form was submitted by an unknown submit button.
|
||||
This will be the case if the form was submitted via a JavaScript
|
||||
action, which is the case when an option select widget is selected.
|
||||
The value of <tt class="literal"><span class="pre">get_submit</span></tt> is <tt class="literal"><span class="pre">True</span></tt> in this case and if it is,
|
||||
you want to clear any errors and re-render the form.</li>
|
||||
<li>If the form has not been submitted or if the form has errors,
|
||||
you simply want to render the form.</li>
|
||||
<li>Check for your named submit buttons which you expect for
|
||||
successful form posting e.g. <tt class="literal"><span class="pre">add</span></tt> or <tt class="literal"><span class="pre">update</span></tt>. If one of
|
||||
these is pressed, call you action inner function.</li>
|
||||
<li>Finally, return a redirect to the expected page following a
|
||||
form submission.</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<p>These steps are illustrated by the following snippet of code and to a
|
||||
large degree in the above functional form2 code example. Often this
|
||||
<tt class="literal"><span class="pre">handle</span></tt> block of code can be simplified. For example, if you do not
|
||||
expect form submissions from unregistered submit buttons, you can
|
||||
eliminate the test for that. Similarly, if your form does not do any
|
||||
widget-specific error checking, there's no reason to have an error
|
||||
checking <tt class="literal"><span class="pre">process</span></tt> function or the call to it:</p>
|
||||
<pre class="literal-block">
|
||||
exit_path = request.get_path(1) + '/'
|
||||
submit = form.get_submit()
|
||||
if not submit:
|
||||
return render()
|
||||
if submit == 'cancel':
|
||||
return request.redirect(exit_path)
|
||||
if submit == True:
|
||||
form.clear_errors()
|
||||
return render()
|
||||
process()
|
||||
if form.has_errors():
|
||||
return render()
|
||||
action(submit)
|
||||
return request.redirect(exit_path)
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,47 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Multi-Threaded Quixote Applications</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="multi-threaded-quixote-applications">
|
||||
<h1 class="title">Multi-Threaded Quixote Applications</h1>
|
||||
<p>Starting with Quixote 0.6, it's possible to write multi-threaded Quixote
|
||||
applications. In previous versions, Quixote stored the current
|
||||
HTTPRequest object in a global variable, meaning that processing
|
||||
multiple requests in the same process simultaneously was impossible.</p>
|
||||
<p>However, the Publisher class as shipped still can't handle multiple
|
||||
simultaneous requests; you'll need to subclass Publisher to make it
|
||||
re-entrant. Here's a starting point:</p>
|
||||
<pre class="literal-block">
|
||||
import thread
|
||||
from quixote.publish import Publisher
|
||||
|
||||
[...]
|
||||
|
||||
class ThreadedPublisher (Publisher):
|
||||
def __init__ (self, root_namespace, config=None):
|
||||
Publisher.__init__(self, root_namespace, config)
|
||||
self._request_dict = {}
|
||||
|
||||
def _set_request(self, request):
|
||||
self._request_dict[thread.get_ident()] = request
|
||||
|
||||
def _clear_request(self):
|
||||
try:
|
||||
del self._request_dict[thread.get_ident()]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def get_request(self):
|
||||
return self._request_dict.get(thread.get_ident())
|
||||
</pre>
|
||||
<p>Using ThreadedPublisher, you now have one current request per thread,
|
||||
rather than one for the entire process.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,156 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Quixote Programming Overview</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="quixote-programming-overview">
|
||||
<h1 class="title">Quixote Programming Overview</h1>
|
||||
<p>This document explains how a Quixote application is structured.
|
||||
The demo.txt file should probably be read before you read this file.
|
||||
There are three components to a Quixote application:</p>
|
||||
<ol class="arabic">
|
||||
<li><p class="first">A driver script, usually a CGI or FastCGI script. This is the
|
||||
interface between your web server (eg., Apache) and the bulk of your
|
||||
application code. The driver script is responsible for creating a
|
||||
Quixote publisher customized for your application and invoking its
|
||||
publishing loop.</p>
|
||||
</li>
|
||||
<li><p class="first">A configuration file. This file specifies various features of the
|
||||
Publisher class, such as how errors are handled, the paths of
|
||||
various log files, and various other things. Read through
|
||||
quixote/config.py for the full list of configuration settings.</p>
|
||||
<p>The most important configuration parameters are:</p>
|
||||
<blockquote>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">ERROR_EMAIL</span></tt></dt>
|
||||
<dd><p class="first last">e-mail address to which errors will be mailed</p>
|
||||
</dd>
|
||||
<dt><tt class="literal"><span class="pre">ERROR_LOG</span></tt></dt>
|
||||
<dd><p class="first last">file to which errors will be logged</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</blockquote>
|
||||
<p>For development/debugging, you should also set <tt class="literal"><span class="pre">DISPLAY_EXCEPTIONS</span></tt>
|
||||
true; the default value is false, to favor security over convenience.</p>
|
||||
</li>
|
||||
<li><p class="first">Finally, the bulk of the code will be called through a call (by the
|
||||
Publisher) to the _q_traverse() method of an instance designated as
|
||||
the <tt class="literal"><span class="pre">root_directory</span></tt>. Normally, the root_directory will be an
|
||||
instance of the Directory class.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="section" id="driver-script">
|
||||
<h1><a name="driver-script">Driver script</a></h1>
|
||||
<p>The driver script is the interface between your web server and Quixote's
|
||||
"publishing loop", which in turn is the gateway to your application
|
||||
code. Thus, there are two things that your Quixote driver script must
|
||||
do:</p>
|
||||
<ul class="simple">
|
||||
<li>create a Quixote publisher -- that is, an instance of the Publisher
|
||||
class provided by the quixote.publish module -- and customize it for
|
||||
your application</li>
|
||||
<li>invoke the publisher's process_request() method as needed to get
|
||||
responses for one or more requests, writing the responses back
|
||||
to the client(s).</li>
|
||||
</ul>
|
||||
<p>The publisher is responsible for translating URLs to Python objects and
|
||||
calling the appropriate function, method, or PTL template to retrieve
|
||||
the information and/or carry out the action requested by the URL.</p>
|
||||
<p>The most important application-specific customization done by the driver
|
||||
script is to set the root directory of your application.</p>
|
||||
<p>The quixote.servers package includes driver modules for cgi, fastcgi,
|
||||
scgi, medusa, twisted, and the simple_server. Each of these modules
|
||||
includes a <tt class="literal"><span class="pre">run()</span></tt> function that you can use in a driver script that
|
||||
provides a function to create the publisher that you want. For an example
|
||||
of this pattern, see the __main__ part of demo/mini_demo.py. You could
|
||||
run the mini_demo.py with scgi by using the <tt class="literal"><span class="pre">run()</span></tt> function imported
|
||||
from quixote.server.scgi_server instead of the one from
|
||||
quixote.server.simple_server. (You would also need your http server
|
||||
set up to use the scgi server.)</p>
|
||||
<p>That's almost the simplest possible case -- there's no
|
||||
application-specific configuration info apart from the root directory.</p>
|
||||
<p>Getting the driver script to actually run is between you and your web
|
||||
server. See the web-server.txt document for help.</p>
|
||||
</div>
|
||||
<div class="section" id="configuration-file">
|
||||
<h1><a name="configuration-file">Configuration file</a></h1>
|
||||
<p>By default, the Publisher uses the configuration information from
|
||||
quixote/config.py. You should never edit the default values in
|
||||
quixote/config.py, because your edits will be lost if you upgrade to a
|
||||
newer Quixote version. You should certainly read it, though, to
|
||||
understand what all the configuration variables are. If you want to
|
||||
customize any of the configuration variables, your driver script
|
||||
should provide your customized Config instance as an argument to the
|
||||
Publisher constructor.</p>
|
||||
</div>
|
||||
<div class="section" id="logging">
|
||||
<h1><a name="logging">Logging</a></h1>
|
||||
<p>The publisher also accepts an optional <tt class="literal"><span class="pre">logger</span></tt> keyword argument,
|
||||
that should, if provided, support the same methods as the
|
||||
default value, an instance of <tt class="literal"><span class="pre">DefaultLogger</span></tt>. Even if you
|
||||
use the default logger, you can still customize the behavior
|
||||
by setting configuration values for <tt class="literal"><span class="pre">access_log</span></tt>, <tt class="literal"><span class="pre">error_log</span></tt>, and/or
|
||||
<tt class="literal"><span class="pre">error_email</span></tt>. These configuration variables are described
|
||||
more fully in config.py.</p>
|
||||
<p>Quixote writes one (rather long) line to the access log for each request
|
||||
it handles; we have split that line up here to make it easier to read:</p>
|
||||
<pre class="literal-block">
|
||||
127.0.0.1 - 2001-10-15 09:48:43
|
||||
2504 "GET /catalog/ HTTP/1.1"
|
||||
200 'Opera/6.0 (Linux; U)' 0.10sec
|
||||
</pre>
|
||||
<p>This line consists of:</p>
|
||||
<ul class="simple">
|
||||
<li>client IP address</li>
|
||||
<li>current user (according to Quixote session management mechanism,
|
||||
so this will be "-" unless you're using a session manager that
|
||||
does authentication)</li>
|
||||
<li>date and time of request in local timezone, as YYYY-MM-DD hh:mm:ss</li>
|
||||
<li>process ID of the process serving the request (eg. your CGI/FastCGI
|
||||
driver script)</li>
|
||||
<li>the HTTP request line (request method, URI, and protocol)</li>
|
||||
<li>response status code</li>
|
||||
<li>HTTP user agent string (specifically, this is
|
||||
<tt class="literal"><span class="pre">repr(os.environ.get('HTTP_USER_AGENT',</span> <span class="pre">''))</span></tt>)</li>
|
||||
<li>time to complete the request</li>
|
||||
</ul>
|
||||
<p>If no access log is configured (ie., <tt class="literal"><span class="pre">ACCESS_LOG</span></tt> is <tt class="literal"><span class="pre">None</span></tt>), then
|
||||
Quixote will not do any access logging.</p>
|
||||
<p>The error log is used for three purposes:</p>
|
||||
<ul class="simple">
|
||||
<li>application output to <tt class="literal"><span class="pre">sys.stdout</span></tt> and <tt class="literal"><span class="pre">sys.stderr</span></tt> goes to
|
||||
Quixote's error log</li>
|
||||
<li>application tracebacks will be written to Quixote's error log</li>
|
||||
</ul>
|
||||
<p>If no error log is configured (with <tt class="literal"><span class="pre">ERROR_LOG</span></tt>), then all output is
|
||||
redirected to the stderr supplied to Quixote for this request by your
|
||||
web server. At least for CGI/FastCGI scripts under Apache, this winds
|
||||
up in Apache's error log.</p>
|
||||
<p>Having stdout redirected to the error log is useful for debugging. You
|
||||
can just sprinkle <tt class="literal"><span class="pre">print</span></tt> statements into your application and the
|
||||
output will wind up in the error log.</p>
|
||||
</div>
|
||||
<div class="section" id="application-code">
|
||||
<h1><a name="application-code">Application code</a></h1>
|
||||
<p>Finally, we reach the most complicated part of a Quixote application.
|
||||
However, thanks to Quixote's design, everything you've ever learned
|
||||
about designing and writing Python code is applicable, so there are no
|
||||
new hoops to jump through. You may, optionally, wish to use PTL,
|
||||
which is simply Python with a novel way of generating function return
|
||||
values -- see PTL.txt for details.</p>
|
||||
<p>Quixote's Publisher constructs a request, splits the path into a list
|
||||
of components, and calls the root directory's _q_traverse() method,
|
||||
giving the component list as an argument. The _q_traverse() will either
|
||||
return a value that will become the content of the HTTPResponse, or
|
||||
else it may raise an Exception. Exceptions are caught by the Publisher
|
||||
and handled as needed, depending on configuration variables and
|
||||
whether or not the Exception is an instance of PublisherError.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,307 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Quixote Session Management</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="quixote-session-management">
|
||||
<h1 class="title">Quixote Session Management</h1>
|
||||
<p>HTTP was originally designed as a stateless protocol, meaning that every
|
||||
request for a document or image was conducted in a separate TCP
|
||||
connection, and that there was no way for a web server to tell if two
|
||||
separate requests actually come from the same user. It's no longer
|
||||
necessarily true that every request is conducted in a separate TCP
|
||||
connection, but HTTP is still fundamentally stateless. However, there
|
||||
are many applications where it is desirable or even essential to
|
||||
establish a "session" for each user, ie. where all requests performed by
|
||||
that user are somehow tied together on the server.</p>
|
||||
<p>HTTP cookies were invented to address this requirement, and they are
|
||||
still the best solution for establishing sessions on top of HTTP. Thus,
|
||||
the session management mechanism that comes with Quixote is
|
||||
cookie-based. (The most common alternative is to embed the session
|
||||
identifier in the URL. Since Quixote views the URL as a fundamental
|
||||
part of the web user interface, a URL-based session management scheme is
|
||||
considered un-Quixotic.)</p>
|
||||
<p>For further reading: the standard for cookies that is approximately
|
||||
implemented by most current browsers is RFC 2109; the latest version of
|
||||
the standard is RFC 2965.</p>
|
||||
<p>In a nutshell, session management with Quixote works like this:</p>
|
||||
<ul>
|
||||
<li><p class="first">when a user-agent first requests a page from a Quixote application
|
||||
that implements session management, Quixote creates a Session object
|
||||
and generates a session ID (a random 64-bit number). The Session
|
||||
object is attached to the current HTTPRequest object, so that
|
||||
application code involved in processing this request has access to
|
||||
the Session object. The get_session() function provides uniform
|
||||
access to the current Session object.</p>
|
||||
</li>
|
||||
<li><p class="first">if, at the end of processing that request, the application code has
|
||||
stored any information in the Session object, Quixote saves the
|
||||
session in its SessionManager object for use by future requests and
|
||||
sends a session cookie, called <tt class="literal"><span class="pre">QX_session</span></tt> by default, to the user.
|
||||
The session cookie contains the session ID encoded as a hexadecimal
|
||||
string, and is included in the response headers, eg.</p>
|
||||
<pre class="literal-block">
|
||||
Set-Cookie: QX_session="928F82A9B8FA92FD"
|
||||
</pre>
|
||||
<p>(You can instruct Quixote to specify the domain and path for
|
||||
URLs to which this cookie should be sent.)</p>
|
||||
</li>
|
||||
<li><p class="first">the user agent stores this cookie for future requests</p>
|
||||
</li>
|
||||
<li><p class="first">the next time the user agent requests a resource that matches the
|
||||
cookie's domain and path, it includes the <tt class="literal"><span class="pre">QX_session</span></tt> cookie
|
||||
previously generated by Quixote in the request headers, eg.:</p>
|
||||
<pre class="literal-block">
|
||||
Cookie: QX_session="928F82A9B8FA92FD"
|
||||
</pre>
|
||||
</li>
|
||||
<li><p class="first">while processing the request, Quixote decodes the session ID and
|
||||
looks up the corresponding Session object in its SessionManager. If
|
||||
there is no such session, the session cookie is bogus or
|
||||
out-of-date, so Quixote raises SessionError; ultimately the user
|
||||
gets an error page. Otherwise, the Session object is made
|
||||
available, through the get_session() function, as the application
|
||||
code processes the request.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>There are two caveats to keep in mind before proceeding, one major and
|
||||
one minor:</p>
|
||||
<ul class="simple">
|
||||
<li>Quixote's standard Session and SessionManager class do not
|
||||
implement any sort of persistence, meaning that all sessions
|
||||
disappear when the process handling web requests terminates.
|
||||
Thus, session management is completely useless with a plain
|
||||
CGI driver script unless you add some persistence to the mix;
|
||||
see "Session persistence" below for information.</li>
|
||||
<li>Quixote never expires sessions; if you want user sessions to
|
||||
be cleaned up after a period of inactivity, you will have to
|
||||
write code to do it yourself.</li>
|
||||
</ul>
|
||||
<div class="section" id="session-management-demo">
|
||||
<h1><a name="session-management-demo">Session management demo</a></h1>
|
||||
<p>There's a simple demo of Quixote's session management in demo/altdemo.py.
|
||||
If the durus (<a class="reference" href="http://www.mems-exchange.org/software/durus/">http://www.mems-exchange.org/software/durus/</a>) package is
|
||||
installed, the demo uses a durus database to store sessions, so sessions
|
||||
will be preserved, even if your are running it with plain cgi.</p>
|
||||
<p>This particular application uses sessions to keep track of just two
|
||||
things: the user's identity and the number of requests made in this
|
||||
session. The first is addressed by Quixote's standard Session class --
|
||||
every Session object has a <tt class="literal"><span class="pre">user</span></tt> attribute, which you can use for
|
||||
anything you like. In the session demo, we simply store a string, the
|
||||
user's name, which is entered by the user.</p>
|
||||
<p>Tracking the number of requests is a bit more interesting: from the
|
||||
DemoSession class in altdemo.py:</p>
|
||||
<pre class="literal-block">
|
||||
def __init__ (self, id):
|
||||
Session.__init__(self, id)
|
||||
self.num_requests = 0
|
||||
|
||||
def start_request (self):
|
||||
Session.start_request(self)
|
||||
self.num_requests += 1
|
||||
</pre>
|
||||
<p>When the session is created, we initialize the request counter; and
|
||||
when we start processing each request, we increment it. Using the
|
||||
session information in the application code is simple. If you want the
|
||||
value of the user attribute of the current session, just call
|
||||
get_user(). If you want some other attribute or method Use
|
||||
get_session() to get the current Session if you need access to other
|
||||
attributes (such as <tt class="literal"><span class="pre">num_requests</span></tt> in the demo) or methods of the
|
||||
current Session instance.</p>
|
||||
<p>Note that the Session class initializes the user attribute to None,
|
||||
so get_user() will return None if no user has been identified for
|
||||
this session. Application code can use this to change behavior,
|
||||
as in the following:</p>
|
||||
<pre class="literal-block">
|
||||
if not get_user():
|
||||
content += htmltext('<p>%s</p>' % href('login', 'login'))
|
||||
else:
|
||||
content += htmltext(
|
||||
'<p>Hello, %s.</p>') % get_user()
|
||||
content += htmltext('<p>%s</p>' % href('logout', 'logout'))
|
||||
</pre>
|
||||
<p>Note that we must quote the user's name, because they are free to enter
|
||||
anything they please, including special HTML characters like <tt class="literal"><span class="pre">&</span></tt> or
|
||||
<tt class="literal"><span class="pre"><</span></tt>.</p>
|
||||
<p>Of course, <tt class="literal"><span class="pre">session.user</span></tt> will never be set if we don't set it
|
||||
ourselves. The code that processes the login form is just this (from
|
||||
<tt class="literal"><span class="pre">login()</span></tt> in <tt class="literal"><span class="pre">demo/altdemo.py</span></tt>)</p>
|
||||
<pre class="literal-block">
|
||||
if get_field("name"):
|
||||
session = get_session()
|
||||
session.set_user(get_field("name")) # This is the important part.
|
||||
</pre>
|
||||
<p>This is obviously a very simple application -- we're not doing any
|
||||
verification of the user's input. We have no user database, no
|
||||
passwords, and no limitations on what constitutes a "user name". A real
|
||||
application would have all of these, as well as a way for users to add
|
||||
themselves to the user database -- ie. register with your web site.</p>
|
||||
</div>
|
||||
<div class="section" id="configuring-the-session-cookie">
|
||||
<h1><a name="configuring-the-session-cookie">Configuring the session cookie</a></h1>
|
||||
<p>Quixote allows you to configure several aspects of the session cookie
|
||||
that it exchanges with clients. First, you can set the name of the
|
||||
cookie; this is important if you have multiple independent Quixote
|
||||
applications running on the same server. For example, the config file
|
||||
for the first application might have</p>
|
||||
<pre class="literal-block">
|
||||
SESSION_COOKIE_NAME = "foo_session"
|
||||
</pre>
|
||||
<p>and the second application might have</p>
|
||||
<pre class="literal-block">
|
||||
SESSION_COOKIE_NAME = "bar_session"
|
||||
</pre>
|
||||
<p>Next, you can use <tt class="literal"><span class="pre">SESSION_COOKIE_DOMAIN</span></tt> and <tt class="literal"><span class="pre">SESSION_COOKIE_PATH</span></tt>
|
||||
to set the cookie attributes that control which requests the cookie is
|
||||
included with. By default, these are both <tt class="literal"><span class="pre">None</span></tt>, which instructs
|
||||
Quixote to send the cookie without <tt class="literal"><span class="pre">Domain</span></tt> or <tt class="literal"><span class="pre">Path</span></tt> qualifiers.
|
||||
For example, if the client requests <tt class="literal"><span class="pre">/foo/bar/</span></tt> from
|
||||
www.example.com, and Quixote decides that it must set the session
|
||||
cookie in the response to that request, then the server would send</p>
|
||||
<pre class="literal-block">
|
||||
Set-Cookie: QX_session="928F82A9B8FA92FD"
|
||||
</pre>
|
||||
<p>in the response headers. Since no domain or path were specified with
|
||||
that cookie, the browser will only include the cookie with requests to
|
||||
www.example.com for URIs that start with <tt class="literal"><span class="pre">/foo/bar/</span></tt>.</p>
|
||||
<p>If you want to ensure that your session cookie is included with all
|
||||
requests to www.example.com, you should set <tt class="literal"><span class="pre">SESSION_COOKIE_PATH</span></tt> in your
|
||||
config file:</p>
|
||||
<pre class="literal-block">
|
||||
SESSION_COOKIE_PATH = "/"
|
||||
</pre>
|
||||
<p>which will cause Quixote to set the cookie like this:</p>
|
||||
<pre class="literal-block">
|
||||
Set-Cookie: QX_session="928F82A9B8FA92FD"; Path="/"
|
||||
</pre>
|
||||
<p>which will instruct the browser to include that cookie with <em>all</em>
|
||||
requests to www.example.com.</p>
|
||||
<p>However, think carefully about what you set <tt class="literal"><span class="pre">SESSION_COOKIE_PATH</span></tt> to
|
||||
-- eg. if you set it to "/", but all of your Quixote code is under "/q/"
|
||||
in your server's URL-space, then your user's session cookies could be
|
||||
unnecessarily exposed. On shared servers where you don't control all of
|
||||
the code, this is especially dangerous; be sure to use (eg.)</p>
|
||||
<pre class="literal-block">
|
||||
SESSION_COOKIE_PATH = "/q/"
|
||||
</pre>
|
||||
<p>on such servers. The trailing slash is important; without it, your
|
||||
session cookies will be sent to URIs like <tt class="literal"><span class="pre">/qux</span></tt> and <tt class="literal"><span class="pre">/qix</span></tt>, even if
|
||||
you don't control those URIs.</p>
|
||||
<p>If you want to share the cookie across servers in your domain,
|
||||
eg. www1.example.com and www2.example.com, you'll also need to set
|
||||
<tt class="literal"><span class="pre">SESSION_COOKIE_DOMAIN</span></tt>:</p>
|
||||
<blockquote>
|
||||
SESSION_COOKIE_DOMAIN = ".example.com"</blockquote>
|
||||
<p>Finally, note that the <tt class="literal"><span class="pre">SESSION_COOKIE_*</span></tt> configuration variables
|
||||
<em>only</em> affect Quixote's session cookie; if you set your own cookies
|
||||
using the <tt class="literal"><span class="pre">HTTPResponse.set_cookie()</span></tt> method, then the cookie sent to
|
||||
the client is completely determined by that <tt class="literal"><span class="pre">set_cookie()</span></tt> call.</p>
|
||||
<p>See RFCs 2109 and 2965 for more information on the rules browsers are
|
||||
supposed to follow for including cookies with HTTP requests.</p>
|
||||
</div>
|
||||
<div class="section" id="writing-the-session-class">
|
||||
<h1><a name="writing-the-session-class">Writing the session class</a></h1>
|
||||
<p>You will almost certainly have to write a custom session class for your
|
||||
application by subclassing Quixote's standard Session class. Every
|
||||
custom session class has two essential responsibilities:</p>
|
||||
<ul class="simple">
|
||||
<li>initialize the attributes that will be used by your application</li>
|
||||
<li>override the <tt class="literal"><span class="pre">has_info()</span></tt> method, so the session manager knows when
|
||||
it must save your session object</li>
|
||||
</ul>
|
||||
<p>The first one is fairly obvious and just good practice. The second is
|
||||
essential, and not at all obvious. The has_info() method exists because
|
||||
SessionManager does not automatically hang on to all session objects;
|
||||
this is a defense against clients that ignore cookies, making your
|
||||
session manager create lots of session objects that are just used once.
|
||||
As long as those session objects are not saved, the burden imposed by
|
||||
these clients is not too bad -- at least they aren't sucking up your
|
||||
memory, or bogging down the database that you save session data to.
|
||||
Thus, the session manager uses has_info() to know if it should hang on
|
||||
to a session object or not: if a session has information that must be
|
||||
saved, the session manager saves it and sends a session cookie to the
|
||||
client.</p>
|
||||
<p>For development/testing work, it's fine to say that your session objects
|
||||
should always be saved:</p>
|
||||
<pre class="literal-block">
|
||||
def has_info (self):
|
||||
return 1
|
||||
</pre>
|
||||
<p>The opposite extreme is to forget to override <tt class="literal"><span class="pre">has_info()</span></tt> altogether,
|
||||
in which case session management most likely won't work: unless you
|
||||
tickle the Session object such that the base <tt class="literal"><span class="pre">has_info()</span></tt> method
|
||||
returns true, the session manager won't save the sessions that it
|
||||
creates, and Quixote will never drop a session cookie on the client.</p>
|
||||
<p>In a real application, you need to think carefully about what data to
|
||||
store in your sessions, and how <tt class="literal"><span class="pre">has_info()</span></tt> should react to the
|
||||
presence of that data. If you try and track something about every
|
||||
single visitor to your site, sooner or later one of those a
|
||||
broken/malicious client that ignores cookies and <tt class="literal"><span class="pre">robots.txt</span></tt> will
|
||||
come along and crawl your entire site, wreaking havoc on your Quixote
|
||||
application (or the database underlying it).</p>
|
||||
</div>
|
||||
<div class="section" id="session-persistence">
|
||||
<h1><a name="session-persistence">Session persistence</a></h1>
|
||||
<p>Keeping session data across requests is all very nice, but in the real
|
||||
world you want that data to survive across process termination. With
|
||||
CGI, this is essential, since each process serves exactly one request
|
||||
and then terminates. With other execution mechanisms, though, it's
|
||||
still important -- you don't want to lose all your session data just
|
||||
because your long-lived server process was restarted, or your server
|
||||
machine was rebooted.</p>
|
||||
<p>However, every application is different, so Quixote doesn't provide any
|
||||
built-in mechanism for session persistence. Instead, it provides a
|
||||
number of hooks, most in the SessionManager class, that let you plug in
|
||||
your preferred persistence mechanism.</p>
|
||||
<p>The first and most important hook is in the SessionManager
|
||||
constructor: you can provide an alternate mapping object that
|
||||
SessionManager will use to store session objects in. By default,
|
||||
SessionManager uses an ordinary dictionary; if you provide a mapping
|
||||
object that implements persistence, then your session data will
|
||||
automatically persist across processes.</p>
|
||||
<p>The second hook (two hooks, really) apply if you use a transactional
|
||||
persistence mechanism to provide your SessionManager's mapping. The
|
||||
<tt class="literal"><span class="pre">altdemo.py</span></tt> script does this with Durus, if the durus package is
|
||||
installed, but you could also use ZODB or a relational database for
|
||||
this purpose. The hooks make sure that session (and other) changes
|
||||
get committed or aborted at the appropriate times. SessionManager
|
||||
provides two methods for you to override: <tt class="literal"><span class="pre">forget_changes()</span></tt> and
|
||||
<tt class="literal"><span class="pre">commit_changes()</span></tt>. <tt class="literal"><span class="pre">forget_changes()</span></tt> is called by
|
||||
SessionPublisher whenever a request crashes, ie. whenever your
|
||||
application raises an exception other than PublishError.
|
||||
<tt class="literal"><span class="pre">commit_changes()</span></tt> is called for requests that complete
|
||||
successfully, or that raise a PublishError exception. You'll have to
|
||||
use your own SessionManager subclass if you need to take advantage of
|
||||
these hooks for transactional session persistence.</p>
|
||||
<p>The third available hook is the Session's is_dirty() method. This is
|
||||
used when your mapping class uses a more primitive storage mechanism,
|
||||
as, for example, the standard 'shelve' module, which provides a
|
||||
mapping object on top of a DBM or Berkeley DB file:</p>
|
||||
<pre class="literal-block">
|
||||
import shelve
|
||||
sessions = shelve.open("/tmp/quixote-sessions")
|
||||
session_manager = SessionManager(session_mapping=sessions)
|
||||
</pre>
|
||||
<p>If you use one of these relatively simple persistent mapping types,
|
||||
you'll also need to override <tt class="literal"><span class="pre">is_dirty()</span></tt> in your Session class.
|
||||
That's in addition to overriding <tt class="literal"><span class="pre">has_info()</span></tt>, which determines if a
|
||||
session object is <em>ever</em> saved; <tt class="literal"><span class="pre">is_dirty()</span></tt> is only called on
|
||||
sessions that have already been added to the session mapping, to see
|
||||
if they need to be "re-added". The default implementation always
|
||||
returns false, because once an object has been added to a normal
|
||||
dictionary, there's no need to add it again. However, with simple
|
||||
persistent mapping types like shelve, you need to store the object
|
||||
again each time it changes. Thus, <tt class="literal"><span class="pre">is_dirty()</span></tt> should return true
|
||||
if the session object needs to be re-written. For a simple, naive,
|
||||
but inefficient implementation, making is_dirty an alias for
|
||||
<tt class="literal"><span class="pre">has_info()</span></tt> will work -- that just means that once the session has
|
||||
been written once, it will be re-written on every request.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,55 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Examples of serving static files</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="examples-of-serving-static-files">
|
||||
<h1 class="title">Examples of serving static files</h1>
|
||||
<p>The <tt class="literal"><span class="pre">quixote.util</span></tt> module includes classes for making files and
|
||||
directories available as Quixote resources. Here are some examples.</p>
|
||||
<div class="section" id="publishing-a-single-file">
|
||||
<h1><a name="publishing-a-single-file">Publishing a Single File</a></h1>
|
||||
<p>The <tt class="literal"><span class="pre">StaticFile</span></tt> class makes an individual filesystem file (possibly
|
||||
a symbolic link) available. You can also specify the MIME type and
|
||||
encoding of the file; if you don't specify this, the MIME type will be
|
||||
guessed using the standard Python <tt class="literal"><span class="pre">mimetypes.guess_type()</span></tt> function.
|
||||
The default action is to not follow symbolic links, but this behaviour
|
||||
can be changed using the <tt class="literal"><span class="pre">follow_symlinks</span></tt> parameter.</p>
|
||||
<p>The following example publishes a file with the URL <tt class="literal"><span class="pre">.../stylesheet_css</span></tt>:</p>
|
||||
<pre class="literal-block">
|
||||
# 'stylesheet_css' must be in the _q_exports list
|
||||
_q_exports = [ ..., 'stylesheet_css', ...]
|
||||
|
||||
stylesheet_css = StaticFile(
|
||||
"/htdocs/legacy_app/stylesheet.css",
|
||||
follow_symlinks=1, mime_type="text/css")
|
||||
</pre>
|
||||
<p>If you want the URL of the file to have a <tt class="literal"><span class="pre">.css</span></tt> extension, you use
|
||||
the external to internal name mapping feature of <tt class="literal"><span class="pre">_q_exports</span></tt>. For
|
||||
example:</p>
|
||||
<pre class="literal-block">
|
||||
_q_exports = [ ..., ('stylesheet.css', 'stylesheet_css'), ...]
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="publishing-a-directory">
|
||||
<h1><a name="publishing-a-directory">Publishing a Directory</a></h1>
|
||||
<p>Publishing a directory is similar. The <tt class="literal"><span class="pre">StaticDirectory</span></tt> class
|
||||
makes a complete filesystem directory available. Again, the default
|
||||
behaviour is to not follow symlinks. You can also request that the
|
||||
<tt class="literal"><span class="pre">StaticDirectory</span></tt> object cache information about the files in
|
||||
memory so that it doesn't try to guess the MIME type on every hit.</p>
|
||||
<p>This example publishes the <tt class="literal"><span class="pre">notes/</span></tt> directory:</p>
|
||||
<pre class="literal-block">
|
||||
_q_exports = [ ..., 'notes', ...]
|
||||
|
||||
notes = StaticDirectory("/htdocs/legacy_app/notes")
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,293 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Upgrading code from older versions of Quixote</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="upgrading-code-from-older-versions-of-quixote">
|
||||
<h1 class="title">Upgrading code from older versions of Quixote</h1>
|
||||
<p>This document lists backward-incompatible changes in Quixote, and
|
||||
explains how to update application code to work with the newer
|
||||
version.</p>
|
||||
<div class="section" id="changes-from-1-0-to-2-0">
|
||||
<h1><a name="changes-from-1-0-to-2-0">Changes from 1.0 to 2.0</a></h1>
|
||||
<p>Change any imports you have from quixote.form to be from quixote.form1.</p>
|
||||
<p>Change any imports you have from quixote.form2 to be from quixote.form.</p>
|
||||
<p>Replace calls to HTTPRequest.get_form_var() with calls to get_field().</p>
|
||||
<p>Define a create_publisher() function to get the publisher you need
|
||||
and figure out how you want to connect it to web server.
|
||||
See files in demo and server for examples. Note that publish1.py
|
||||
contains a publisher that works more like the Quixote1 Publisher,
|
||||
and does not require the changes listed below.</p>
|
||||
<p>Make every namespace be an instance of quixote.directory.Directory.
|
||||
Update namespaces that are modules (or in the init.py of a package) by
|
||||
defining a new class in the module that inherits from Directory and
|
||||
moving your _q_exports and _q_* functions onto the class. Replace
|
||||
"request" parameters with "self" parameters on the new methods. If
|
||||
you have a _q_resolve method, include Resolving in the bases of your
|
||||
new class.</p>
|
||||
<p>Remove request from calls to _q_ functions. If request, session,
|
||||
user, path, or redirect is used in these new methods, replace as
|
||||
needed with calls to get_request(), get_session(), get_user(),
|
||||
get_path(), and/or redirect(), imported from quixote.</p>
|
||||
<p>In every namespace that formerly traversed into a module, import the
|
||||
new Directory class from the module and create an instance of the
|
||||
Directory in a variable whose name is the name of the module.</p>
|
||||
<p>In every namespace with a _q_exports and a _q_index, either add "" to
|
||||
_q_exports or make sure that _q_lookup handles "" by returning the result
|
||||
of a call to _q_index.</p>
|
||||
<p>If your code depends on the Publisher's namespace_stack attribute,
|
||||
try using quixote.util.get_directory_path() instead. If you need the
|
||||
namespace stack after the traversal, override Directory._q_traverse()
|
||||
to call get_directory_path() when the end of the path is reached, and
|
||||
record the result somewhere for later reference.</p>
|
||||
<p>If your code depends on _q_exception_handler, override the _q_traverse
|
||||
on your root namespace or on your own Directory class to catch exceptions
|
||||
and handle them the way you want. If you just want a general customization
|
||||
for exception responses, you can change or override
|
||||
Publisher.format_publish_error().</p>
|
||||
<p>If your code depended on _q_access, include the AccessControlled with
|
||||
the bases of your Directory classes as needed.</p>
|
||||
<p>Provide imports as needed to htmltext, TemplateIO, get_field,
|
||||
get_request, get_session, get_user, get_path, redirect, ?. You may
|
||||
find dulcinea/bin/unknown.py useful for identifying missing imports.</p>
|
||||
<p>Quixote 1's secure_errors configuration variable is not present in Quixote 2.</p>
|
||||
<p>Form.__init__ no longer has name or attrs keywords. If your existing
|
||||
code calls Form.__init__ with 'attrs=foo', you'll need to change it to
|
||||
'<a href="#id1" name="id2"><span class="problematic" id="id2">**</span></a>foo'. Form instances no longer have a name attribute. If your code
|
||||
looks for form.name, you can find it with form.attrs.get('name').
|
||||
The Form.__init__ keyword parameter (and attribute) 'action_url' is now
|
||||
named 'action'.</p>
|
||||
<div class="system-message" id="id1">
|
||||
<p class="system-message-title">System Message: <a name="id1">WARNING/2</a> (<tt>upgrading.txt</tt>, line 65); <em><a href="#id2">backlink</a></em></p>
|
||||
Inline strong start-string without end-string.</div>
|
||||
<p>The SessionPublisher class is gone. Use the Publisher class instead.
|
||||
Also, the 'session_mgr' keyword has been renamed to 'session_manager'.</p>
|
||||
</div>
|
||||
<div class="section" id="changes-from-0-6-1-to-1-0">
|
||||
<h1><a name="changes-from-0-6-1-to-1-0">Changes from 0.6.1 to 1.0</a></h1>
|
||||
<div class="section" id="sessions">
|
||||
<h2><a name="sessions">Sessions</a></h2>
|
||||
<p>A leading underscore was removed from the <tt class="literal"><span class="pre">Session</span></tt> attributes
|
||||
<tt class="literal"><span class="pre">__remote_address</span></tt>, <tt class="literal"><span class="pre">__creation_time</span></tt>, and <tt class="literal"><span class="pre">__access_time</span></tt>. If
|
||||
you have pickled <tt class="literal"><span class="pre">Session</span></tt> objects you will need to upgrade them
|
||||
somehow. Our preferred method is to write a script that unpickles each
|
||||
object, renames the attributes and then re-pickles it.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="changes-from-0-6-to-0-6-1">
|
||||
<h1><a name="changes-from-0-6-to-0-6-1">Changes from 0.6 to 0.6.1</a></h1>
|
||||
<div class="section" id="q-exception-handler-now-called-if-exception-while-traversing">
|
||||
<h2><a name="q-exception-handler-now-called-if-exception-while-traversing"><tt class="literal"><span class="pre">_q_exception_handler</span></tt> now called if exception while traversing</a></h2>
|
||||
<p><tt class="literal"><span class="pre">_q_exception_handler</span></tt> hooks will now be called if an exception is
|
||||
raised during the traversal process. Quixote 0.6 had a bug that caused
|
||||
<tt class="literal"><span class="pre">_q_exception_handler</span></tt> hooks to only be called if an exception was
|
||||
raised after the traversal completed.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="changes-from-0-5-to-0-6">
|
||||
<h1><a name="changes-from-0-5-to-0-6">Changes from 0.5 to 0.6</a></h1>
|
||||
<div class="section" id="q-getname-renamed-to-q-lookup">
|
||||
<h2><a name="q-getname-renamed-to-q-lookup"><tt class="literal"><span class="pre">_q_getname</span></tt> renamed to <tt class="literal"><span class="pre">_q_lookup</span></tt></a></h2>
|
||||
<p>The <tt class="literal"><span class="pre">_q_getname</span></tt> special function was renamed to <tt class="literal"><span class="pre">_q_lookup</span></tt>,
|
||||
because that name gives a clearer impression of the function's
|
||||
purpose. In 0.6, <tt class="literal"><span class="pre">_q_getname</span></tt> still works but will trigger a
|
||||
warning.</p>
|
||||
</div>
|
||||
<div class="section" id="form-framework-changes">
|
||||
<h2><a name="form-framework-changes">Form Framework Changes</a></h2>
|
||||
<p>The <tt class="literal"><span class="pre">quixote.form.form</span></tt> module was changed from a .ptl file to a .py
|
||||
file. You should delete or move the existing <tt class="literal"><span class="pre">quixote/</span></tt> directory
|
||||
in <tt class="literal"><span class="pre">site-packages</span></tt> before running <tt class="literal"><span class="pre">setup.py</span></tt>, or at least delete
|
||||
the old <tt class="literal"><span class="pre">form.ptl</span></tt> and <tt class="literal"><span class="pre">form.ptlc</span></tt> files.</p>
|
||||
<p>The widget and form classes in the <tt class="literal"><span class="pre">quixote.form</span></tt> package now return
|
||||
<tt class="literal"><span class="pre">htmltext</span></tt> instances. Applications that use forms and widgets will
|
||||
likely have to be changed to use the <tt class="literal"><span class="pre">[html]</span></tt> template type to avoid
|
||||
over-escaping of HTML special characters.</p>
|
||||
<p>Also, the constructor arguments to <tt class="literal"><span class="pre">SelectWidget</span></tt> and its subclasses have
|
||||
changed. This only affects applications that use the form framework
|
||||
located in the <tt class="literal"><span class="pre">quixote.form</span></tt> package.</p>
|
||||
<p>In Quixote 0.5, the <tt class="literal"><span class="pre">SelectWidget</span></tt> constructor had this signature:</p>
|
||||
<pre class="literal-block">
|
||||
def __init__ (self, name, value=None,
|
||||
allowed_values=None,
|
||||
descriptions=None,
|
||||
size=None,
|
||||
sort=0):
|
||||
</pre>
|
||||
<p><tt class="literal"><span class="pre">allowed_values</span></tt> was the list of objects that the user could choose,
|
||||
and <tt class="literal"><span class="pre">descriptions</span></tt> was a list of strings that would actually be
|
||||
shown to the user in the generated HTML.</p>
|
||||
<p>In Quixote 0.6, the signature has changed slightly:</p>
|
||||
<pre class="literal-block">
|
||||
def __init__ (self, name, value=None,
|
||||
allowed_values=None,
|
||||
descriptions=None,
|
||||
options=None,
|
||||
size=None,
|
||||
sort=0):
|
||||
</pre>
|
||||
<p>The <tt class="literal"><span class="pre">quote</span></tt> argument is gone, and the <tt class="literal"><span class="pre">options</span></tt> argument has been
|
||||
added. If an <tt class="literal"><span class="pre">options</span></tt> argument is provided, <tt class="literal"><span class="pre">allowed_values</span></tt>
|
||||
and <tt class="literal"><span class="pre">descriptions</span></tt> must not be supplied.</p>
|
||||
<p>The <tt class="literal"><span class="pre">options</span></tt> argument, if present, must be a list of tuples with
|
||||
1,2, or 3 elements, of the form <tt class="literal"><span class="pre">(value:any,</span> <span class="pre">description:any,</span>
|
||||
<span class="pre">key:string)</span></tt>.</p>
|
||||
<blockquote>
|
||||
<ul class="simple">
|
||||
<li><tt class="literal"><span class="pre">value</span></tt> is the object that will be returned if the user chooses
|
||||
this item, and must always be supplied.</li>
|
||||
<li><tt class="literal"><span class="pre">description</span></tt> is a string or htmltext instance which will be
|
||||
shown to the user in the generated HTML. It will be passed
|
||||
through the htmlescape() functions, so for an ordinary string
|
||||
special characters such as '&' will be converted to '&amp;'.
|
||||
htmltext instances will be left as they are.</li>
|
||||
<li>If supplied, <tt class="literal"><span class="pre">key</span></tt> will be used in the value attribute
|
||||
of the option element (<tt class="literal"><span class="pre"><option</span> <span class="pre">value="..."></span></tt>).
|
||||
If not supplied, keys will be generated; <tt class="literal"><span class="pre">value</span></tt> is checked for a
|
||||
<tt class="literal"><span class="pre">_p_oid</span></tt> attribute and if present, that string is used;
|
||||
otherwise the description is used.</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p>In the common case, most applications won't have to change anything,
|
||||
though the ordering of selection items may change due to the
|
||||
difference in how keys are generated.</p>
|
||||
</div>
|
||||
<div class="section" id="file-upload-changes">
|
||||
<h2><a name="file-upload-changes">File Upload Changes</a></h2>
|
||||
<p>Quixote 0.6 introduces new support for HTTP upload requests. Any HTTP
|
||||
request with a Content-Type of "multipart/form-data" -- which is
|
||||
generally only used for uploads -- is now represented by
|
||||
HTTPUploadRequest, a subclass of HTTPRequest, and the uploaded files
|
||||
themselves are represented by Upload objects.</p>
|
||||
<p>Whenever an HTTP request has a Content-Type of "multipart/form-data",
|
||||
an instance of HTTPUploadRequest is created instead of HTTPRequest.
|
||||
Some of the fields in the request are presumably uploaded files and
|
||||
might be quite large, so HTTPUploadRequest will read all of the fields
|
||||
supplied in the request body and write them out to temporary files;
|
||||
the temporary files are written in the directory specified by the
|
||||
UPLOAD_DIR configuration variable.</p>
|
||||
<p>Once the temporary files have been written, the HTTPUploadRequest
|
||||
object is passed to a function or PTL template, just like an ordinary
|
||||
request. The difference between HTTPRequest and HTTPUploadRequest
|
||||
is that all of the form variables are represented as Upload objects.
|
||||
Upload objects have three attributes:</p>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">orig_filename</span></tt></dt>
|
||||
<dd>the filename supplied by the browser.</dd>
|
||||
<dt><tt class="literal"><span class="pre">base_filename</span></tt></dt>
|
||||
<dd>a stripped-down version of orig_filename with unsafe characters removed.
|
||||
This could be used when writing uploaded data to a permanent location.</dd>
|
||||
<dt><tt class="literal"><span class="pre">tmp_filename</span></tt></dt>
|
||||
<dd>the path of the temporary file containing the uploaded data for this field.</dd>
|
||||
</dl>
|
||||
<p>Consult upload.txt for more information about handling file uploads.</p>
|
||||
</div>
|
||||
<div class="section" id="refactored-publisher-class">
|
||||
<h2><a name="refactored-publisher-class">Refactored <cite>Publisher</cite> Class</a></h2>
|
||||
<p>Various methods in the <cite>Publisher</cite> class were rearranged. If your
|
||||
application subclasses Publisher, you may need to change your code
|
||||
accordingly.</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li><p class="first"><tt class="literal"><span class="pre">parse_request()</span></tt> no longer creates the HTTPRequest object;
|
||||
instead a new method, <tt class="literal"><span class="pre">create_request()</span></tt>, handles this,
|
||||
and can be overridden as required.</p>
|
||||
<p>As a result, the method signature has changed from
|
||||
<tt class="literal"><span class="pre">parse_request(stdin,</span> <span class="pre">env)</span></tt> to <tt class="literal"><span class="pre">parse_request(request)</span></tt>.</p>
|
||||
</li>
|
||||
<li><p class="first">The <tt class="literal"><span class="pre">Publisher.publish()</span></tt> method now catches exceptions raised
|
||||
by <tt class="literal"><span class="pre">parse_request()</span></tt>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="changes-from-0-4-to-0-5">
|
||||
<h1><a name="changes-from-0-4-to-0-5">Changes from 0.4 to 0.5</a></h1>
|
||||
<div class="section" id="session-management-changes">
|
||||
<h2><a name="session-management-changes">Session Management Changes</a></h2>
|
||||
<p>The Quixote session management interface underwent lots of change and
|
||||
cleanup with Quixote 0.5. It was previously undocumented (apart from
|
||||
docstrings in the code), so we thought that this was a good opportunity
|
||||
to clean up the interface. Nevertheless, those brave souls who got
|
||||
session management working just by reading the code are in for a bit of
|
||||
suffering; this brief note should help clarify things. The definitive
|
||||
documentation for session management is session-mgmt.txt -- you should
|
||||
start there.</p>
|
||||
<div class="section" id="attribute-renamings-and-pickled-objects">
|
||||
<h3><a name="attribute-renamings-and-pickled-objects">Attribute renamings and pickled objects</a></h3>
|
||||
<p>Most attributes of the standard Session class were made private in order
|
||||
to reduce collisions with subclasses. The downside is that pickled
|
||||
Session objects will break. You might want to (temporarily) modify
|
||||
session.py and add this method to Session:</p>
|
||||
<pre class="literal-block">
|
||||
def __setstate__ (self, dict):
|
||||
# Update for attribute renamings made in rev. 1.51.2.3
|
||||
# (between Quixote 0.4.7 and 0.5).
|
||||
self.__dict__.update(dict)
|
||||
if hasattr(self, 'remote_address'):
|
||||
self.__remote_address = self.remote_address
|
||||
del self.remote_address
|
||||
if hasattr(self, 'creation_time'):
|
||||
self.__creation_time = self.creation_time
|
||||
del self.creation_time
|
||||
if hasattr(self, 'access_time'):
|
||||
self.__access_time = self.access_time
|
||||
del self.access_time
|
||||
if hasattr(self, 'form_tokens'):
|
||||
self._form_tokens = self.form_tokens
|
||||
del self.form_tokens
|
||||
</pre>
|
||||
<p>However, if your sessions were pickled via ZODB, this may not work. (It
|
||||
didn't work for us.) In that case, you'll have to add something like
|
||||
this to your class that inherits from both ZODB's Persistent and
|
||||
Quixote's Session:</p>
|
||||
<pre class="literal-block">
|
||||
def __setstate__ (self, dict):
|
||||
# Blechhh! This doesn't work if I put it in Quixote's
|
||||
# session.py, so I have to second-guess how Python
|
||||
# treats "__" attribute names.
|
||||
self.__dict__.update(dict)
|
||||
if hasattr(self, 'remote_address'):
|
||||
self._Session__remote_address = self.remote_address
|
||||
del self.remote_address
|
||||
if hasattr(self, 'creation_time'):
|
||||
self._Session__creation_time = self.creation_time
|
||||
del self.creation_time
|
||||
if hasattr(self, 'access_time'):
|
||||
self._Session__access_time = self.access_time
|
||||
del self.access_time
|
||||
if hasattr(self, 'form_tokens'):
|
||||
self._form_tokens = self.form_tokens
|
||||
del self.form_tokens
|
||||
</pre>
|
||||
<p>It's not pretty, but it worked for us.</p>
|
||||
</div>
|
||||
<div class="section" id="cookie-domains-and-paths">
|
||||
<h3><a name="cookie-domains-and-paths">Cookie domains and paths</a></h3>
|
||||
<p>The session cookie config variables -- <tt class="literal"><span class="pre">COOKIE_NAME</span></tt>,
|
||||
<tt class="literal"><span class="pre">COOKIE_DOMAIN</span></tt>, and <tt class="literal"><span class="pre">COOKIE_PATH</span></tt> -- have been renamed to
|
||||
<tt class="literal"><span class="pre">SESSION_COOKIE_*</span></tt> for clarity.</p>
|
||||
<p>If you previously set the config variable <tt class="literal"><span class="pre">COOKIE_DOMAIN</span></tt> to the name
|
||||
of your server, this is most likely no longer necessary -- it's now fine
|
||||
to leave <tt class="literal"><span class="pre">SESSION_COOKIE_DOMAIN</span></tt> unset (ie. <tt class="literal"><span class="pre">None</span></tt>), which
|
||||
ultimately means browsers will only include the session cookie in
|
||||
requests to the same server that sent it to them in the first place.</p>
|
||||
<p>If you previously set <tt class="literal"><span class="pre">COOKIE_PATH</span></tt>, then you should probably preserve
|
||||
your setting as <tt class="literal"><span class="pre">SESSION_COOKIE_PATH</span></tt>. The default of <tt class="literal"><span class="pre">None</span></tt> means
|
||||
that browsers will only send session cookies with requests for URIs
|
||||
under the URI that originally resulted in the session cookie being sent.
|
||||
See session-mgmt.txt and RFCs 2109 and 2965.</p>
|
||||
<p>If you previously set <tt class="literal"><span class="pre">COOKIE_NAME</span></tt>, change it to
|
||||
<tt class="literal"><span class="pre">SESSION_COOKIE_NAME</span></tt>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,229 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Web Server Configuration for Quixote</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="web-server-configuration-for-quixote">
|
||||
<h1 class="title">Web Server Configuration for Quixote</h1>
|
||||
<p>For a simple Quixote installation, there are two things you have to get
|
||||
right:</p>
|
||||
<ul class="simple">
|
||||
<li>installation of the Quixote modules to Python's library (the
|
||||
trick here is that the <tt class="literal"><span class="pre">quixote</span></tt> package must be visible to the user
|
||||
that CGI scripts run as, not necessarily to you as an interactive
|
||||
command-line user)</li>
|
||||
<li>configuration of your web server to run Quixote driver scripts</li>
|
||||
</ul>
|
||||
<p>This document is concerned with the second of these.</p>
|
||||
<div class="section" id="which-web-servers">
|
||||
<h1><a name="which-web-servers">Which web servers?</a></h1>
|
||||
<p>We are only familiar with Apache, and we develop Quixote for use under
|
||||
Apache. However, Quixote doesn't rely on any Apache-specific tricks;
|
||||
if you can execute CGI scripts, then you can run Quixote applications
|
||||
(although they'll run a lot faster with mod_scgi or FastCGI). If you
|
||||
can redirect arbitrary URLs to a CGI script and preserve parts of the
|
||||
URL as an add-on to the script name (with <tt class="literal"><span class="pre">PATH_INFO</span></tt>), then you can
|
||||
run Quixote applications in the ideal manner, ie. with superfluous
|
||||
implementation details hidden from the user.</p>
|
||||
</div>
|
||||
<div class="section" id="which-operating-systems">
|
||||
<h1><a name="which-operating-systems">Which operating systems?</a></h1>
|
||||
<p>We are mainly familiar with Unix, and develop and deploy Quixote under
|
||||
Linux. However, we've had several reports of people using Quixote under
|
||||
Windows, more-or-less successfully. There are still a few Unix-isms in
|
||||
the code, but they are being rooted out in favor of portability.</p>
|
||||
<p>Remember that your system is only as secure as its weakest link.
|
||||
Quixote can't help you write secure web applications on an inherently
|
||||
insecure operating system.</p>
|
||||
</div>
|
||||
<div class="section" id="basic-cgi-configuration">
|
||||
<h1><a name="basic-cgi-configuration">Basic CGI configuration</a></h1>
|
||||
<p>Throughout this document, I'm going to assume that:</p>
|
||||
<ul class="simple">
|
||||
<li>CGI scripts live in the <tt class="literal"><span class="pre">/www/cgi-bin</span></tt> directory of your web server,
|
||||
and have the extension <tt class="literal"><span class="pre">.cgi</span></tt></li>
|
||||
<li>HTTP requests for <tt class="literal"><span class="pre">/cgi-bin/foo.cgi</span></tt> will result in the execution
|
||||
of <tt class="literal"><span class="pre">/www/cgi-bin/foo.cgi</span></tt> (for various values of <tt class="literal"><span class="pre">foo</span></tt>)</li>
|
||||
<li>if the web server is instructed to serve an executable file
|
||||
<tt class="literal"><span class="pre">bar.cgi</span></tt>, the file is treated as a CGI script</li>
|
||||
</ul>
|
||||
<p>With Apache, these configuration directives will do the trick:</p>
|
||||
<pre class="literal-block">
|
||||
AddHandler cgi-script .cgi
|
||||
ScriptAlias /cgi-bin/ /www/cgi-bin/
|
||||
</pre>
|
||||
<p>Consult the Apache documentation for other ways of configuring CGI
|
||||
script execution.</p>
|
||||
<p>For other web servers, consult your server's documentation.</p>
|
||||
</div>
|
||||
<div class="section" id="installing-driver-scripts">
|
||||
<h1><a name="installing-driver-scripts">Installing driver scripts</a></h1>
|
||||
<p>Given the above configuration, installing a Quixote driver script is the
|
||||
same as installing any other CGI script: copy it to <tt class="literal"><span class="pre">/www/cgi-bin</span></tt> (or
|
||||
whatever). To install the Quixote demo's cgi driver script:</p>
|
||||
<pre class="literal-block">
|
||||
cp -p server/cgi_server.py /www/cgi-bin/demo.cgi
|
||||
</pre>
|
||||
<p>(The <tt class="literal"><span class="pre">-p</span></tt> option ensures that <tt class="literal"><span class="pre">cp</span></tt> preserves the file mode, so that
|
||||
it remains executable.)</p>
|
||||
</div>
|
||||
<div class="section" id="url-rewriting">
|
||||
<h1><a name="url-rewriting">URL rewriting</a></h1>
|
||||
<p>With the above configuration, users need to use URLs like</p>
|
||||
<pre class="literal-block">
|
||||
http://www.example.com/cgi-bin/demo.cgi
|
||||
</pre>
|
||||
<p>to access the Quixote demo (or other Quixote applications installed in
|
||||
the same way). This works, but it's ugly and unnecessarily exposes
|
||||
implementation details.</p>
|
||||
<p>In our view, it's preferable to give each Quixote application its own
|
||||
chunk of URL-space -- a "virtual directory" if you like. For example,
|
||||
you might want</p>
|
||||
<pre class="literal-block">
|
||||
http://www.example.com/qdemo
|
||||
</pre>
|
||||
<p>to handle the Quixote demo.</p>
|
||||
<p>With Apache, this is quite easy, as long as mod_rewrite is compiled,
|
||||
loaded, and enabled. (Building and loading Apache modules is beyond the
|
||||
scope of this document; consult the Apache documentation.)</p>
|
||||
<p>To enable the rewrite engine, use the</p>
|
||||
<pre class="literal-block">
|
||||
RewriteEngine on
|
||||
</pre>
|
||||
<p>directive. If you have virtual hosts, make sure to repeat this for each
|
||||
<tt class="literal"><span class="pre"><VirtualHost></span></tt> section of your config file.</p>
|
||||
<p>The rewrite rule to use in this case is</p>
|
||||
<pre class="literal-block">
|
||||
RewriteRule ^/qdemo(/.*) /www/cgi-bin/demo.cgi$1 [last]
|
||||
</pre>
|
||||
<p>This is <em>not</em> a redirect; this is all handled with one HTTP
|
||||
request/response cycle, and the user never sees <tt class="literal"><span class="pre">/cgi-bin/demo.cgi</span></tt> in
|
||||
a URL.</p>
|
||||
<p>Note that requests for <tt class="literal"><span class="pre">/qdemo/</span></tt> and <tt class="literal"><span class="pre">/qdemo</span></tt> are <em>not</em> the same; in
|
||||
particular, with the above rewrite rule, the former will succeed and the
|
||||
latter will not. (Look at the regex again if you don't believe me:
|
||||
<tt class="literal"><span class="pre">/qdemo</span></tt> doesn't match the regex, so <tt class="literal"><span class="pre">demo.cgi</span></tt> is never invoked.)</p>
|
||||
<p>The solution for <tt class="literal"><span class="pre">/qdemo</span></tt> is the same as if it corresponded to a
|
||||
directory in your document tree: redirect it to <tt class="literal"><span class="pre">/qdemo/</span></tt>. Apache
|
||||
(and, presumably, other web servers) does this automatically for "real"
|
||||
directories; however, <tt class="literal"><span class="pre">/qdemo/</span></tt> is just a directory-like chunk of
|
||||
URL-space, so either you or Quixote have to take care of the redirect.</p>
|
||||
<p>It's almost certainly faster for you to take care of it in the web
|
||||
server's configuration. With Apache, simply insert this directive
|
||||
<em>before</em> the above rewrite rule:</p>
|
||||
<pre class="literal-block">
|
||||
RewriteRule ^/qdemo$ /qdemo/ [redirect=permanent]
|
||||
</pre>
|
||||
<p>If, for some reason, you are unwilling or unable to instruct your web
|
||||
server to perform this redirection, Quixote will do it for you.
|
||||
However, you have to make sure that the <tt class="literal"><span class="pre">/qdemo</span></tt> URL is handled by
|
||||
Quixote. Change the rewrite rule to:</p>
|
||||
<pre class="literal-block">
|
||||
RewriteRule ^/qdemo(/.*)?$ /www/cgi-bin/demo.cgi$1 [last]
|
||||
</pre>
|
||||
<p>Now a request for <tt class="literal"><span class="pre">/qdemo</span></tt> will be handled by Quixote, and it will
|
||||
generate a redirect to <tt class="literal"><span class="pre">/qdemo/</span></tt>. If you're using a CGI driver
|
||||
script, this will be painfully slow, but it will work.</p>
|
||||
<p>For redirecting and rewriting URLs with other web servers, consult your
|
||||
server's documentation.</p>
|
||||
</div>
|
||||
<div class="section" id="long-running-processes">
|
||||
<h1><a name="long-running-processes">Long-running processes</a></h1>
|
||||
<p>For serious web applications, CGI is unacceptably slow. For a CGI-based
|
||||
Quixote application, you have to start a Python interpreter, load the
|
||||
Quixote modules, and load your application's modules before you can
|
||||
start working. For sophisticated, database-backed applications, you'll
|
||||
probably have to open a new database connection as well for every hit.</p>
|
||||
<p>Small wonder so many high-performance alternatives to CGI exist. (The
|
||||
main advantages of CGI are that it is widely supported and easy to
|
||||
develop with. Even for large Quixote applications, running in CGI mode
|
||||
is nice in development because you don't have to kill a long-running
|
||||
driver script every time the code changes.) Quixote includes support
|
||||
for mod_scgi and FastCGI.</p>
|
||||
</div>
|
||||
<div class="section" id="mod-scgi-configuration">
|
||||
<h1><a name="mod-scgi-configuration">mod_scgi configuration</a></h1>
|
||||
<p>SCGI is a CGI replacement written by Neil Schemenauer, one of
|
||||
Quixote's developers, and is similar to FastCGI but is designed to be
|
||||
easier to implement. mod_scgi simply forwards requests to an
|
||||
already-running SCGI server on a different TCP port, and doesn't try
|
||||
to start or stop processes, leaving that up to the SCGI server.</p>
|
||||
<p>The SCGI code is available from <a class="reference" href="http://www.mems-exchange.org/software/scgi/">http://www.mems-exchange.org/software/scgi/</a> .</p>
|
||||
<p>The quixote.server.scgi_server module is a script that
|
||||
publishes the demo quixote application via SCGI. You can use
|
||||
it for your application by importing it and calling the <tt class="literal"><span class="pre">run()</span></tt>
|
||||
function with arguments to run your application, on the port
|
||||
you choose. Here is an example:</p>
|
||||
<pre class="literal-block">
|
||||
#!/usr/bin/python
|
||||
from quixote.server.scgi_server import run
|
||||
from quixote.publish import Publisher
|
||||
from mymodule import MyRootDirectory
|
||||
|
||||
def create_my_publisher():
|
||||
return Publisher(MyRootDirectory())
|
||||
|
||||
run(create_my_publisher, port=3001)
|
||||
</pre>
|
||||
<p>The following Apache directive will direct requests to an SCGI server
|
||||
running on port 3001:</p>
|
||||
<pre class="literal-block">
|
||||
<Location />
|
||||
SCGIServer 127.0.0.1 3001
|
||||
SCGIHandler On
|
||||
</Location>
|
||||
</pre>
|
||||
<p>[Note: the mod_scgi module for Apache 2 requires a colon, instead of a
|
||||
space, between the host and port on the SCGIServer line.]</p>
|
||||
</div>
|
||||
<div class="section" id="scgi-through-cgi">
|
||||
<h1><a name="scgi-through-cgi">SCGI through CGI</a></h1>
|
||||
<p>Recent releases of the scgi package include cgi2scgi.c, a small program
|
||||
that offers an extremely convenient way to take advantage of SCGI using
|
||||
Apache or any web server that supports CGI. To use it, compile the
|
||||
cgi2scgi.c and install the compiled program as usual for your
|
||||
webserver. The default SCGI port is 3000, but you can change that
|
||||
by adding <tt class="literal"><span class="pre">-DPORT=3001</span></tt> (for example) to your compile command.</p>
|
||||
<p>Although this method requires a new process to be launched for each
|
||||
request, the process is small and fast, so the performance is
|
||||
acceptable for many applications.</p>
|
||||
</div>
|
||||
<div class="section" id="fastcgi-configuration">
|
||||
<h1><a name="fastcgi-configuration">FastCGI configuration</a></h1>
|
||||
<p>If your web server supports FastCGI, you can significantly speed up your
|
||||
Quixote applications with a simple change to your configuration. You
|
||||
don't have to change your code at all (unless it makes assumptions about
|
||||
how many requests are handled by each process). (See
|
||||
<a class="reference" href="http://www.fastcgi.com/">http://www.fastcgi.com/</a> for more information on FastCGI.)</p>
|
||||
<p>To use FastCGI with Apache, you'll need to download mod_fastcgi from
|
||||
<a class="reference" href="http://www.fastcgi.com/">http://www.fastcgi.com/</a> and add it to your Apache installation.</p>
|
||||
<p>Configuring a FastCGI driver script is best done after reading the fine
|
||||
documentation for mod_fastcgi at
|
||||
<a class="reference" href="http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html">http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html</a></p>
|
||||
<p>However, if you just want to try it with the Quixote demo to see if it
|
||||
works, add this directive to your Apache configuration:</p>
|
||||
<pre class="literal-block">
|
||||
AddHandler fastcgi-script .fcgi
|
||||
</pre>
|
||||
<p>and copy server/fastcgi_server.py to demo.fcgi. If you're using a URL
|
||||
rewrite to map requests for (eg.) <tt class="literal"><span class="pre">/qdemo</span></tt> to
|
||||
<tt class="literal"><span class="pre">/www/cgi-bin/demo.cgi</span></tt>, be sure to change the rewrite -- it should
|
||||
now point to <tt class="literal"><span class="pre">/www/cgi-bin/demo.fcgi</span></tt>.</p>
|
||||
<p>After the first access to <tt class="literal"><span class="pre">demo.fcgi</span></tt> (or <tt class="literal"><span class="pre">/qdemo/</span></tt> with the
|
||||
modified rewrite rule), the demo should be noticeably faster. You
|
||||
should also see a <tt class="literal"><span class="pre">demo.fcgi</span></tt> process running if you do <tt class="literal"><span class="pre">ps</span> <span class="pre">-le</span></tt>
|
||||
(<tt class="literal"><span class="pre">ps</span> <span class="pre">-aux</span></tt> on BSD-ish systems, or maybe <tt class="literal"><span class="pre">ps</span> <span class="pre">aux</span></tt>). (On my 800 MHz
|
||||
Athlon machine, there are slight but perceptible delays navigating the
|
||||
Quixote demo in CGI mode. In FastCGI mode, the delay between pages is
|
||||
no longer perceptible -- navigation is instantaneous.) The larger your
|
||||
application is, the more code it loads, and the more work it does at
|
||||
startup, the bigger a win FastCGI will be for you (in comparison to CGI).</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,190 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Implementing Web Services with Quixote</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="implementing-web-services-with-quixote">
|
||||
<h1 class="title">Implementing Web Services with Quixote</h1>
|
||||
<p>This document will show you how to implement Web services using
|
||||
Quixote.</p>
|
||||
<div class="section" id="an-xml-rpc-service">
|
||||
<h1><a name="an-xml-rpc-service">An XML-RPC Service</a></h1>
|
||||
<p>XML-RPC is the simplest protocol commonly used to expose a Web
|
||||
service. In XML-RPC, there are a few basic data types such as
|
||||
integers, floats, strings, and dates, and a few aggregate types such
|
||||
as arrays and structs. The xmlrpclib module, part of the Python 2.2
|
||||
standard library and available separately from
|
||||
<a class="reference" href="http://www.pythonware.com/products/xmlrpc/">http://www.pythonware.com/products/xmlrpc/</a>, converts between Python's
|
||||
standard data types and the XML-RPC data types.</p>
|
||||
<table class="table" frame="border" rules="all">
|
||||
<colgroup>
|
||||
<col width="40%" />
|
||||
<col width="60%" />
|
||||
</colgroup>
|
||||
<tbody valign="top">
|
||||
<tr><td>XML-RPC Type</td>
|
||||
<td>Python Type or Class</td>
|
||||
</tr>
|
||||
<tr><td><int></td>
|
||||
<td>int</td>
|
||||
</tr>
|
||||
<tr><td><double></td>
|
||||
<td>float</td>
|
||||
</tr>
|
||||
<tr><td><string></td>
|
||||
<td>string</td>
|
||||
</tr>
|
||||
<tr><td><array></td>
|
||||
<td>list</td>
|
||||
</tr>
|
||||
<tr><td><struct></td>
|
||||
<td>dict</td>
|
||||
</tr>
|
||||
<tr><td><boolean></td>
|
||||
<td>xmlrpclib.Boolean</td>
|
||||
</tr>
|
||||
<tr><td><base64></td>
|
||||
<td>xmlrpclib.Binary</td>
|
||||
</tr>
|
||||
<tr><td><dateTime></td>
|
||||
<td>xmlrpclib.DateTime</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="section" id="making-xml-rpc-calls">
|
||||
<h1><a name="making-xml-rpc-calls">Making XML-RPC Calls</a></h1>
|
||||
<p>Making an XML-RPC call using xmlrpclib is easy. An XML-RPC server
|
||||
lives at a particular URL, so the first step is to create an
|
||||
xmlrpclib.ServerProxy object pointing at that URL.</p>
|
||||
<pre class="literal-block">
|
||||
>>> import xmlrpclib
|
||||
>>> s = xmlrpclib.ServerProxy(
|
||||
'http://www.stuffeddog.com/speller/speller-rpc.cgi')
|
||||
</pre>
|
||||
<p>Now you can simply make a call to the spell-checking service offered
|
||||
by this server:</p>
|
||||
<pre class="literal-block">
|
||||
>>> s.speller.spellCheck('my speling isnt gud', {})
|
||||
[{'word': 'speling', 'suggestions': ['apeling', 'spelding',
|
||||
'spelling', 'sperling', 'spewing', 'spiling'], 'location': 4},
|
||||
{'word': 'isnt', 'suggestions': [``isn't'', 'ist'], 'location': 12}]
|
||||
>>>
|
||||
</pre>
|
||||
<p>This call results in the following XML being sent:</p>
|
||||
<pre class="literal-block">
|
||||
<?xml version='1.0'?>
|
||||
<methodCall>
|
||||
<methodName>speller.spellCheck</methodName>
|
||||
<params>
|
||||
<param>
|
||||
<value><string>my speling isnt gud</string></value>
|
||||
</param>
|
||||
<param>
|
||||
<value><struct></struct></value>
|
||||
</param>
|
||||
</params>
|
||||
</methodCall>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="writing-a-quixote-service">
|
||||
<h1><a name="writing-a-quixote-service">Writing a Quixote Service</a></h1>
|
||||
<p>In the quixote.util module, Quixote provides a function,
|
||||
<tt class="literal"><span class="pre">xmlrpc(request,</span> <span class="pre">func)</span></tt>, that processes the body of an XML-RPC
|
||||
request. <tt class="literal"><span class="pre">request</span></tt> is the HTTPRequest object that Quixote passes to
|
||||
every function it invokes. <tt class="literal"><span class="pre">func</span></tt> is a user-supplied function that
|
||||
receives the name of the XML-RPC method being called and a tuple
|
||||
containing the method's parameters. If there's a bug in the function
|
||||
you supply and it raises an exception, the <tt class="literal"><span class="pre">xmlrpc()</span></tt> function will
|
||||
catch the exception and return a <tt class="literal"><span class="pre">Fault</span></tt> to the remote caller.</p>
|
||||
<p>Here's an example of implementing a simple XML-RPC handler with a
|
||||
single method, <tt class="literal"><span class="pre">get_time()</span></tt>, that simply returns the current
|
||||
time. The first task is to expose a URL for accessing the service.</p>
|
||||
<pre class="literal-block">
|
||||
from quixote.directory import Directory
|
||||
from quixote.util import xmlrpc
|
||||
from quixote import get_request
|
||||
|
||||
class RPCDirectory(Directory):
|
||||
|
||||
_q_exports = ['rpc']
|
||||
|
||||
def rpc (self):
|
||||
return xmlrpc(get_request(), rpc_process)
|
||||
|
||||
def rpc_process (meth, params):
|
||||
...
|
||||
</pre>
|
||||
<p>When the above code is placed in the __init__.py file for the Python
|
||||
package corresponding to your Quixote application, it exposes the URL
|
||||
<tt class="literal"><span class="pre">http://<hostname>/rpc</span></tt> as the access point for the XML-RPC service.</p>
|
||||
<p>Next, we need to fill in the contents of the <tt class="literal"><span class="pre">rpc_process()</span></tt>
|
||||
function:</p>
|
||||
<pre class="literal-block">
|
||||
import time
|
||||
|
||||
def rpc_process (meth, params):
|
||||
if meth == 'get_time':
|
||||
# params is ignored
|
||||
now = time.gmtime(time.time())
|
||||
return xmlrpclib.DateTime(now)
|
||||
else:
|
||||
raise RuntimeError, "Unknown XML-RPC method: %r" % meth
|
||||
</pre>
|
||||
<p><tt class="literal"><span class="pre">rpc_process()</span></tt> receives the method name and the parameters, and its
|
||||
job is to run the right code for the method, returning a result that
|
||||
will be marshalled into XML-RPC. The body of <tt class="literal"><span class="pre">rpc_process()</span></tt> will
|
||||
therefore usually be an <tt class="literal"><span class="pre">if</span></tt> statement that checks the name of the
|
||||
method, and calls another function to do the actual work. In this case,
|
||||
<tt class="literal"><span class="pre">get_time()</span></tt> is very simple so the two lines of code it requires are
|
||||
simply included in the body of <tt class="literal"><span class="pre">rpc_process()</span></tt>.</p>
|
||||
<p>If the method name doesn't belong to a supported method, execution
|
||||
will fall through to the <tt class="literal"><span class="pre">else</span></tt> clause, which will raise a
|
||||
RuntimeError exception. Quixote's <tt class="literal"><span class="pre">xmlrpc()</span></tt> will catch this
|
||||
exception and report it to the caller as an XML-RPC fault, with the
|
||||
error code set to 1.</p>
|
||||
<p>As you add additional XML-RPC services, the <tt class="literal"><span class="pre">if</span></tt> statement in
|
||||
<tt class="literal"><span class="pre">rpc_process()</span></tt> will grow more branches. You might be tempted to pass
|
||||
the method name to <tt class="literal"><span class="pre">getattr()</span></tt> to select a method from a module or
|
||||
class. That would work, too, and avoids having a continually growing
|
||||
set of branches, but you should be careful with this and be sure that
|
||||
there are no private methods that a remote caller could access. I
|
||||
generally prefer to have the <tt class="literal"><span class="pre">if...</span> <span class="pre">elif...</span> <span class="pre">elif...</span> <span class="pre">else</span></tt> blocks, for
|
||||
three reasons: 1) adding another branch isn't much work, 2) it's
|
||||
explicit about the supported method names, and 3) there won't be any
|
||||
security holes in doing so.</p>
|
||||
<p>An alternative approach is to have a dictionary mapping method names
|
||||
to the corresponding functions and restrict the legal method names
|
||||
to the keys of this dictionary:</p>
|
||||
<pre class="literal-block">
|
||||
def echo (*params):
|
||||
# Just returns the parameters it's passed
|
||||
return params
|
||||
|
||||
def get_time ():
|
||||
now = time.gmtime(time.time())
|
||||
return xmlrpclib.DateTime(now)
|
||||
|
||||
methods = {'echo' : echo,
|
||||
'get_time' : get_time}
|
||||
|
||||
def rpc_process (meth, params):
|
||||
func = methods.get[meth]
|
||||
if methods.has_key(meth):
|
||||
# params is ignored
|
||||
now = time.gmtime(time.time())
|
||||
return xmlrpclib.DateTime(now)
|
||||
else:
|
||||
raise RuntimeError, "Unknown XML-RPC method: %r" % meth
|
||||
</pre>
|
||||
<p>This approach works nicely when there are many methods and the
|
||||
<tt class="literal"><span class="pre">if...elif...else</span></tt> statement would be unworkably long.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
502
doc/widgets.html
502
doc/widgets.html
|
@ -1,502 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.3.0: http://docutils.sourceforge.net/" />
|
||||
<title>Quixote Widget Classes</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="quixote-widget-classes">
|
||||
<h1 class="title">Quixote Widget Classes</h1>
|
||||
<p>[This is reference documentation. If you haven't yet read "Lesson 5:
|
||||
widgets" of demo.txt, you should go and do so now. This document also
|
||||
assumes you have a good understanding of HTML forms and form elements.
|
||||
If not, you could do worse than pick up a copy of <em>HTML: The Definitive
|
||||
Guide</em> by Chuck Musciano & Bill Kennedy (O'Reilly). I usually keep it
|
||||
within arm's reach.]</p>
|
||||
<p>Web forms are built out of form elements: string input, select lists,
|
||||
checkboxes, submit buttons, and so forth. Quixote provides a family of
|
||||
classes for handling these form elements, or widgets, in the
|
||||
quixote.form.widget module. The class hierarchy is:</p>
|
||||
<pre class="literal-block">
|
||||
Widget [A]
|
||||
|
|
||||
+--StringWidget
|
||||
| |
|
||||
| +--PasswordWidget
|
||||
| |
|
||||
| +--NumberWidget [*] [A]
|
||||
| |
|
||||
| +-FloatWidget [*]
|
||||
| +-IntWidget [*]
|
||||
|
|
||||
+--TextWidget
|
||||
|
|
||||
+--CheckboxWidget
|
||||
|
|
||||
+--SelectWidget [A]
|
||||
| |
|
||||
| +--SingleSelectWidget
|
||||
| | |
|
||||
| | +-RadiobuttonsWidget
|
||||
| | |
|
||||
| | +-OptionSelectWidget [*]
|
||||
| |
|
||||
| +--MultipleSelectWidget
|
||||
|
|
||||
+--SubmitButtonWidget
|
||||
|
|
||||
+--HiddenWidget
|
||||
|
|
||||
+--ListWidget [*]
|
||||
|
||||
[*] Widget classes that do not correspond exactly with a particular
|
||||
HTML form element
|
||||
[A] Abstract classes
|
||||
</pre>
|
||||
<div class="section" id="widget-the-base-class">
|
||||
<h1><a name="widget-the-base-class">Widget: the base class</a></h1>
|
||||
<p>Widget is the abstract base class for the widget hierarchy. It provides
|
||||
the following facilities:</p>
|
||||
<ul class="simple">
|
||||
<li>widget name (<tt class="literal"><span class="pre">name</span></tt> attribute, <tt class="literal"><span class="pre">set_name()</span></tt> method)</li>
|
||||
<li>widget value (<tt class="literal"><span class="pre">value</span></tt> attribute, <tt class="literal"><span class="pre">set_value()</span></tt> and <tt class="literal"><span class="pre">clear()</span></tt> methods)</li>
|
||||
<li><tt class="literal"><span class="pre">__str__()</span></tt> and <tt class="literal"><span class="pre">__repr__()</span></tt> methods</li>
|
||||
<li>some facilities for writing composite widget classes</li>
|
||||
</ul>
|
||||
<p>The Widget constructor signature is:</p>
|
||||
<pre class="literal-block">
|
||||
Widget(name : string, value : any = None)
|
||||
</pre>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">name</span></tt></dt>
|
||||
<dd>the name of the widget. For non-compound widgets (ie. everything in
|
||||
the above class hierarchy), this will be used as the "name"
|
||||
attribute for the main HTML tag that defines the form element.</dd>
|
||||
<dt><tt class="literal"><span class="pre">value</span></tt></dt>
|
||||
<dd>the current value of the form element. The type of 'value' depends
|
||||
on the widget class. Most widget classes support only a single
|
||||
type, eg. StringWidget always deals with strings and IntWidget with
|
||||
integers. The SelectWidget classes are different; see the
|
||||
descriptions below for details.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="section" id="common-widget-methods">
|
||||
<h1><a name="common-widget-methods">Common widget methods</a></h1>
|
||||
<p>The Widget base class also provides a couple of useful
|
||||
methods:</p>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">set_name(name:string)</span></tt></dt>
|
||||
<dd>use this to change the widget name supplied to the constructor.
|
||||
Unless you know what you're doing, you should do this before
|
||||
rendering or parsing the widget.</dd>
|
||||
<dt><tt class="literal"><span class="pre">set_value(value:any)</span></tt></dt>
|
||||
<dd>use this to set the widget value; this is the same as supplying
|
||||
a value to the constructor (and the same type rules apply, ie.
|
||||
the type of 'value' depends on the widget class).</dd>
|
||||
<dt><tt class="literal"><span class="pre">clear()</span></tt></dt>
|
||||
<dd>clear the widget's current value. Equivalent to
|
||||
<tt class="literal"><span class="pre">widget.set_value(None)</span></tt>.</dd>
|
||||
</dl>
|
||||
<p>The following two methods will be used on every widget object you
|
||||
create; if you write your own widget classes, you will almost certainly
|
||||
have to define both of these:</p>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">render(request:HTTPRequest)</span></tt> <span class="classifier-delimiter">:</span> <span class="classifier"><tt class="literal"><span class="pre">string</span></tt></span></dt>
|
||||
<dd>return a chunk of HTML that implements the form element
|
||||
corresponding to this widget.</dd>
|
||||
<dt><tt class="literal"><span class="pre">parse(request:HTTPRequest)</span></tt> <span class="classifier-delimiter">:</span> <span class="classifier"><tt class="literal"><span class="pre">any</span></tt></span></dt>
|
||||
<dd>extract the form value for this widget from <tt class="literal"><span class="pre">request.form</span></tt>, parse it
|
||||
according to the rules for this widget class, and return the
|
||||
resulting value. The return value depends on the widget class, and
|
||||
will be of the same type as the value passed to the constructor
|
||||
and/or <tt class="literal"><span class="pre">set_value()</span></tt>.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="section" id="stringwidget">
|
||||
<h1><a name="stringwidget">StringWidget</a></h1>
|
||||
<p>Used for short, single-line string input with no validation (ie. any
|
||||
string will be accepted.) Generates an <tt class="literal"><span class="pre"><input</span> <span class="pre">type="text"></span></tt> form
|
||||
element.</p>
|
||||
<div class="section" id="constructor">
|
||||
<h2><a name="constructor">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
StringWidget(name : string,
|
||||
value : string = None,
|
||||
size : int = None,
|
||||
maxlength : int = None)
|
||||
</pre>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">size</span></tt></dt>
|
||||
<dd>used as the <tt class="literal"><span class="pre">size</span></tt> attribute of the generated <tt class="literal"><span class="pre"><input></span></tt> tag;
|
||||
controls the physical size of the input field.</dd>
|
||||
<dt><tt class="literal"><span class="pre">maxlength</span></tt></dt>
|
||||
<dd>used as the <tt class="literal"><span class="pre">maxlength</span></tt> attribute; controls the maximum amount
|
||||
of input.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="section" id="examples">
|
||||
<h2><a name="examples">Examples</a></h2>
|
||||
<pre class="literal-block">
|
||||
>>> StringWidget("foo", value="hello").render(request)
|
||||
'<input name="foo" type="text" value="hello">'
|
||||
|
||||
>>> StringWidget("foo", size=10, maxlength=20).render(request)
|
||||
'<input name="foo" type="text" size="10" maxlength="20">'
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="passwordwidget">
|
||||
<h1><a name="passwordwidget">PasswordWidget</a></h1>
|
||||
<p>PasswordWidget is identical to StringWidget except for the type of the
|
||||
HTML form element: <tt class="literal"><span class="pre">password</span></tt> instead of <tt class="literal"><span class="pre">text</span></tt>.</p>
|
||||
</div>
|
||||
<div class="section" id="textwidget">
|
||||
<h1><a name="textwidget">TextWidget</a></h1>
|
||||
<p>Used for multi-line text input. The value is a single string with
|
||||
newlines right where the browser supplied them. (<tt class="literal"><span class="pre">\r\n</span></tt>, if present,
|
||||
is converted to <tt class="literal"><span class="pre">\n</span></tt>.) Generates a <tt class="literal"><span class="pre"><textarea></span></tt> form element.</p>
|
||||
<div class="section" id="id1">
|
||||
<h2><a name="id1">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
TextWidget(name : string,
|
||||
value : string = None,
|
||||
cols : int = None,
|
||||
rows : int = None,
|
||||
wrap : string = "physical")
|
||||
</pre>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">cols</span></tt>, <tt class="literal"><span class="pre">rows</span></tt></dt>
|
||||
<dd>number of columns/rows in the textarea</dd>
|
||||
<dt><tt class="literal"><span class="pre">wrap</span></tt></dt>
|
||||
<dd>controls how the browser wraps text and includes newlines in the
|
||||
submitted form value; consult an HTML book for details.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="checkboxwidget">
|
||||
<h1><a name="checkboxwidget">CheckboxWidget</a></h1>
|
||||
<p>Used for single boolean (on/off) value. The value you supply can be
|
||||
anything, since Python has a boolean interpretation for all values; the
|
||||
value returned by <tt class="literal"><span class="pre">parse()</span></tt> will always be 0 or 1 (but you shouldn't
|
||||
rely on that!). Generates an <tt class="literal"><span class="pre"><input</span> <span class="pre">type="checkbox"></span></tt> form element.</p>
|
||||
<div class="section" id="id2">
|
||||
<h2><a name="id2">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
CheckboxWidget(name : string,
|
||||
value : boolean = false)
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="id3">
|
||||
<h2><a name="id3">Examples</a></h2>
|
||||
<pre class="literal-block">
|
||||
>>> CheckboxWidget("foo", value=0).render(request)
|
||||
'<input name="foo" type="checkbox" value="yes">'
|
||||
|
||||
>>> CheckboxWidget("foo", value="you bet").render(request)
|
||||
'<input name="foo" type="checkbox" value="yes" checked>'
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="radiobuttonswidget">
|
||||
<h1><a name="radiobuttonswidget">RadiobuttonsWidget</a></h1>
|
||||
<p>Used for a <em>set</em> of related radiobuttons, ie. several <tt class="literal"><span class="pre"><input</span>
|
||||
<span class="pre">type="radio"></span></tt> tags with the same name and different values. The set
|
||||
of values are supplied to the constructor as <tt class="literal"><span class="pre">allowed_values</span></tt>, which
|
||||
may be a list of any Python objects (not just strings). The current
|
||||
value must be either <tt class="literal"><span class="pre">None</span></tt> (the default) or one of the values in
|
||||
<tt class="literal"><span class="pre">allowed_values</span></tt>; if you supply a <tt class="literal"><span class="pre">value</span></tt> not in <tt class="literal"><span class="pre">allowed_values</span></tt>,
|
||||
it will be ignored. <tt class="literal"><span class="pre">parse()</span></tt> will return either <tt class="literal"><span class="pre">None</span></tt> or one of
|
||||
the values in <tt class="literal"><span class="pre">allowed_values</span></tt>.</p>
|
||||
<div class="section" id="id4">
|
||||
<h2><a name="id4">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
RadiobuttonsWidget(name : string,
|
||||
value : any = None,
|
||||
allowed_values : [any] = None,
|
||||
descriptions : [string] = map(str, allowed_values),
|
||||
quote : boolean = true,
|
||||
delim : string = "\n")
|
||||
</pre>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">allowed_values</span></tt></dt>
|
||||
<dd><p class="first">specifies how many <tt class="literal"><span class="pre"><input</span> <span class="pre">type="radio"></span></tt> tags to generate and the
|
||||
values for each. Eg. <tt class="literal"><span class="pre">allowed_values=["foo",</span> <span class="pre">"bar"]</span></tt> will result in
|
||||
(roughly):</p>
|
||||
<pre class="last literal-block">
|
||||
<input type="radio" value="foo">
|
||||
<input type="radio" value="bar">
|
||||
</pre>
|
||||
</dd>
|
||||
<dt><tt class="literal"><span class="pre">descriptions</span></tt></dt>
|
||||
<dd>the text that will actually be shown to the user in the web page
|
||||
that includes this widget. Handy when the elements of
|
||||
<tt class="literal"><span class="pre">allowed_values</span></tt> are too terse, or don't have a meaningful
|
||||
<tt class="literal"><span class="pre">str()</span></tt>, or you want to add some additional cues for the user. If
|
||||
not supplied, <tt class="literal"><span class="pre">map(str,</span> <span class="pre">allowed_values)</span></tt> is used, with the
|
||||
exception that <tt class="literal"><span class="pre">None</span></tt> in <tt class="literal"><span class="pre">allowed_values</span></tt> becomes <tt class="literal"><span class="pre">""</span></tt> (the
|
||||
empty string) in <tt class="literal"><span class="pre">descriptions</span></tt>. If supplied, <tt class="literal"><span class="pre">descriptions</span></tt>
|
||||
must be the same length as <tt class="literal"><span class="pre">allowed_values</span></tt>.</dd>
|
||||
<dt><tt class="literal"><span class="pre">quote</span></tt></dt>
|
||||
<dd>if true (the default), the elements of 'descriptions' will be
|
||||
HTML-quoted (using <tt class="literal"><span class="pre">quixote.html.html_quote()</span></tt>) when the widget is
|
||||
rendered. This is essential if you might have characters like
|
||||
<tt class="literal"><span class="pre">&</span></tt> or <tt class="literal"><span class="pre"><</span></tt> in your descriptions. However, you'll want to set
|
||||
<tt class="literal"><span class="pre">quote</span></tt> to false if you are deliberately including HTML markup
|
||||
in your descriptions.</dd>
|
||||
<dt><tt class="literal"><span class="pre">delim</span></tt></dt>
|
||||
<dd>the delimiter to separate the radiobuttons with when rendering
|
||||
the whole widget. The default ensures that your HTML is readable
|
||||
(by putting each <tt class="literal"><span class="pre"><input></span></tt> tag on a separate line), and that there
|
||||
is horizontal whitespace between each radiobutton.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="section" id="id5">
|
||||
<h2><a name="id5">Examples</a></h2>
|
||||
<pre class="literal-block">
|
||||
>>> colours = ["red", "green", "blue", "pink"]
|
||||
>>> widget = RadiobuttonsWidget("foo", allowed_values=colours)
|
||||
>>> print widget.render(request)
|
||||
<input name="foo" type="radio" value="0">red</input>
|
||||
<input name="foo" type="radio" value="1">green</input>
|
||||
<input name="foo" type="radio" value="2">blue</input>
|
||||
<input name="foo" type="radio" value="3">pink</input>
|
||||
</pre>
|
||||
<p>(Note that the actual form values, ie. what the browser returns to the
|
||||
server, are always stringified indices into the 'allowed_values' list.
|
||||
This is irrelevant to you, since SingleSelectWidget takes care of
|
||||
converting <tt class="literal"><span class="pre">"1"</span></tt> to <tt class="literal"><span class="pre">1</span></tt> and looking up <tt class="literal"><span class="pre">allowed_values[1]</span></tt>.)</p>
|
||||
<pre class="literal-block">
|
||||
>>> values = [val1, val2, val3]
|
||||
>>> descs = ["thing <b>1</b>",
|
||||
"thing <b>2</b>",
|
||||
"thing <b>3</b>"]
|
||||
>>> widget = RadiobuttonsWidget("bar",
|
||||
allowed_values=values,
|
||||
descriptions=descs,
|
||||
value=val3,
|
||||
delim="<br>\n",
|
||||
quote=0)
|
||||
>>> print widget.render(request)
|
||||
<input name="bar" type="radio" value="0">thing <b>1</b></input><br>
|
||||
<input name="bar" type="radio" value="1">thing <b>2</b></input><br>
|
||||
<input name="bar" type="radio" value="2" checked="checked">thing <b>3</b></input>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="singleselectwidget">
|
||||
<h1><a name="singleselectwidget">SingleSelectWidget</a></h1>
|
||||
<p>Used to select a single value from a list that's too long or ungainly
|
||||
for a set of radiobuttons. (Most browsers implement this as a scrolling
|
||||
list; UNIX versions of Netscape 4.x and earlier used a pop-up menu.)
|
||||
The value can be any Python object; <tt class="literal"><span class="pre">parse()</span></tt> will return either
|
||||
<tt class="literal"><span class="pre">None</span></tt> or one of the values you supply to the constructor as
|
||||
<tt class="literal"><span class="pre">allowed_values</span></tt>. Generates a <tt class="literal"><span class="pre"><select>...</select></span></tt> tag, with one
|
||||
<tt class="literal"><span class="pre"><option></span></tt> tag for each element of <tt class="literal"><span class="pre">allowed_values</span></tt>.</p>
|
||||
<div class="section" id="id6">
|
||||
<h2><a name="id6">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
SingleSelectWidget(name : string,
|
||||
value : any = None,
|
||||
allowed_values : [any] = None,
|
||||
descriptions : [string] = map(str, allowed_values),
|
||||
quote : boolean = true,
|
||||
size : int = None)
|
||||
</pre>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">allowed_values</span></tt></dt>
|
||||
<dd>determines the set of <tt class="literal"><span class="pre"><option></span></tt> tags that will go inside the
|
||||
<tt class="literal"><span class="pre"><select></span></tt> tag; these can be any Python values (not just strings).
|
||||
<tt class="literal"><span class="pre">parse()</span></tt> will return either one of the <tt class="literal"><span class="pre">allowed_values</span></tt> or <tt class="literal"><span class="pre">None</span></tt>.
|
||||
If you supply a <tt class="literal"><span class="pre">value</span></tt> that is not in <tt class="literal"><span class="pre">allowed_values</span></tt>, it
|
||||
will be ignored.</dd>
|
||||
<dt><tt class="literal"><span class="pre">descriptions</span></tt></dt>
|
||||
<dd>(same as RadiobuttonsWidget above)</dd>
|
||||
<dt><tt class="literal"><span class="pre">quote</span></tt></dt>
|
||||
<dd>(same as RadiobuttonsWidget above)</dd>
|
||||
<dt><tt class="literal"><span class="pre">size</span></tt></dt>
|
||||
<dd>corresponds to the <tt class="literal"><span class="pre">size</span></tt> attribute of the <tt class="literal"><span class="pre"><select></span></tt> tag: ask
|
||||
the browser to show a select list with <tt class="literal"><span class="pre">size</span></tt> items visible.
|
||||
Not always respected by the browser; consult an HTML book.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="section" id="id7">
|
||||
<h2><a name="id7">Examples</a></h2>
|
||||
<pre class="literal-block">
|
||||
>>> widget = SingleSelectWidget("foo",
|
||||
allowed_values=["abc", 123, 5.5])
|
||||
>>> print widget.render(request)
|
||||
<select name="foo">
|
||||
<option value="0">abc
|
||||
<option value="1">123
|
||||
<option value="2">5.5
|
||||
</select>
|
||||
|
||||
>>> widget = SingleSelectWidget("bar",
|
||||
value=val2,
|
||||
allowed_values=[val1, val2, val3],
|
||||
descriptions=["foo", "bar", "foo & bar"],
|
||||
size=3)
|
||||
>>> print widget.render(request)
|
||||
<select name="bar" size="3">
|
||||
<option value="0">foo
|
||||
<option selected value="1">bar
|
||||
<option value="2">foo &amp; bar
|
||||
</select>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="multipleselectwidget">
|
||||
<h1><a name="multipleselectwidget">MultipleSelectWidget</a></h1>
|
||||
<p>Used to select multiple values from a list. Everything is just like
|
||||
SingleSelectWidget, except that <tt class="literal"><span class="pre">value</span></tt> can be a list of objects
|
||||
selected from <tt class="literal"><span class="pre">allowed_values</span></tt> (in which case every object in <tt class="literal"><span class="pre">value</span></tt>
|
||||
will initially be selected). Generates a <tt class="literal"><span class="pre"><select</span> <span class="pre">multiple>...</select></span></tt>
|
||||
tag, with one <tt class="literal"><span class="pre"><option></span></tt> tag for each element of <tt class="literal"><span class="pre">allowed_values</span></tt>.</p>
|
||||
<div class="section" id="id8">
|
||||
<h2><a name="id8">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
MultipleSelectWidget(name : string,
|
||||
value : any | [any] = None,
|
||||
allowed_values : [any] = None,
|
||||
descriptions : [string] = map(str, allowed_values),
|
||||
quote : boolean = true,
|
||||
size : int = None)
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="submitbuttonwidget">
|
||||
<h1><a name="submitbuttonwidget">SubmitButtonWidget</a></h1>
|
||||
<p>Used for generating submit buttons. Note that HTML submit buttons are
|
||||
rather weird, and Quixote preserves this weirdness -- the Widget classes
|
||||
are meant to be a fairly thin wrapper around HTML form elements, after
|
||||
all.</p>
|
||||
<p>In particular, the widget value for a submit button controls two things:
|
||||
what the user sees in their browser (the text in the button) and what
|
||||
the browser returns as the value for that form element. You can't
|
||||
control the two separately, as you can with radiobuttons or selection
|
||||
widgets.</p>
|
||||
<p>Also, SubmitButtonWidget is the only widget with an optional <tt class="literal"><span class="pre">name</span></tt>.
|
||||
In many simple forms, all you care about is the fact that the form was
|
||||
submitted -- which submit button the user used doesn't matter.</p>
|
||||
<div class="section" id="id9">
|
||||
<h2><a name="id9">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
SubmitButtonWidget(name : string = None,
|
||||
value : string = None)
|
||||
</pre>
|
||||
<dl>
|
||||
<dt><tt class="literal"><span class="pre">value</span></tt></dt>
|
||||
<dd>the text that will be shown in the user's browser, <em>and</em> the
|
||||
value that will be returned for this form element (widget)
|
||||
if the user selects this submit button.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="section" id="id10">
|
||||
<h2><a name="id10">Examples</a></h2>
|
||||
<blockquote>
|
||||
<pre class="doctest-block">
|
||||
>>> SubmitButtonWidget(value="Submit Form").render(request)
|
||||
'<input type="submit" value="Submit Form">'
|
||||
</pre>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="hiddenwidget">
|
||||
<h1><a name="hiddenwidget">HiddenWidget</a></h1>
|
||||
<p>Used to generate HTML hidden widgets, which can be useful for carrying
|
||||
around non-sensitive application state. (The Quixote form framework
|
||||
uses hidden widgets for form tokens as a measure against cross-site
|
||||
request forgery [CSRF] attacks. So by "sensitive" I mean "information
|
||||
which should not be revealed", rather than "security-related". If you
|
||||
wouldn't put it in a cookie or in email, don't put it in a hidden form
|
||||
element.)</p>
|
||||
<div class="section" id="id11">
|
||||
<h2><a name="id11">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
HiddenWidget(name : string,
|
||||
value : string)
|
||||
</pre>
|
||||
</div>
|
||||
<div class="section" id="id12">
|
||||
<h2><a name="id12">Examples</a></h2>
|
||||
<pre class="literal-block">
|
||||
>>> HiddenWidget("form_id", "2452345135").render(request)
|
||||
'<input type="hidden" name="form_id" value="2452345135">'
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="intwidget">
|
||||
<h1><a name="intwidget">IntWidget</a></h1>
|
||||
<p>The first derived widget class: this is a subclass of StringWidget
|
||||
specifically for entering integer values. As such, this is the first
|
||||
widget class we've covered that can reject certain user input. (The
|
||||
selection widgets all have to validate their input in case of broken or
|
||||
malicious clients, but they just drop bogus values.) If the user enters
|
||||
a string that Python's built-in <tt class="literal"><span class="pre">int()</span></tt> can't convert to an integer,
|
||||
IntWidget's <tt class="literal"><span class="pre">parse()</span></tt> method raises FormValueError (also defined in
|
||||
the quixote.form.widget module). This exception is handled by Quixote's
|
||||
form framework, but if you're using widget objects on their own, you'll
|
||||
have to handle it yourself.</p>
|
||||
<p><tt class="literal"><span class="pre">IntWidget.parse()</span></tt> always returns an integer or <tt class="literal"><span class="pre">None</span></tt>.</p>
|
||||
<div class="section" id="id13">
|
||||
<h2><a name="id13">Constructor</a></h2>
|
||||
<pre class="literal-block">
|
||||
IntWidget(name : string,
|
||||
value : int = None,
|
||||
size : int = None,
|
||||
maxlength : int = None)
|
||||
</pre>
|
||||
<p>Constructor arguments are as for StringWidget, except that <tt class="literal"><span class="pre">value</span></tt>
|
||||
must be an integer (or <tt class="literal"><span class="pre">None</span></tt>). Note that <tt class="literal"><span class="pre">size</span></tt> and
|
||||
<tt class="literal"><span class="pre">maxlength</span></tt> have exactly the same meaning: they control the size of
|
||||
the input widget and the maximum number of characters of input.</p>
|
||||
<p>[Examples]</p>
|
||||
<blockquote>
|
||||
<pre class="doctest-block">
|
||||
>>> IntWidget("num", value=37, size=5).render(request)
|
||||
'<input type="string" name="num" value="37" size="5">'
|
||||
</pre>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="floatwidget">
|
||||
<h1><a name="floatwidget">FloatWidget</a></h1>
|
||||
<p>FloatWidget is identical to IntWidget, except:</p>
|
||||
<ul class="simple">
|
||||
<li><tt class="literal"><span class="pre">value</span></tt> must be a float</li>
|
||||
<li><tt class="literal"><span class="pre">parse()</span></tt> returns a float or <tt class="literal"><span class="pre">None</span></tt></li>
|
||||
<li><tt class="literal"><span class="pre">parse()</span></tt> raises FormValueError if the string entered by the
|
||||
user cannot be converted by Python's built-in <tt class="literal"><span class="pre">float()</span></tt> function</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="optionselectwidget">
|
||||
<h1><a name="optionselectwidget">OptionSelectWidget</a></h1>
|
||||
<p>OptionSelectWidget is simply a SingleSelectWidget that uses a bit of
|
||||
Javascript to automatically submit the current form as soon as the user
|
||||
selects a value. This is useful for very simple one-element forms where
|
||||
you don't want to bother with a submit button, or for very complex forms
|
||||
where you need to revamp the user interface based on a user's selection.
|
||||
Your form-processing code could then detect that style of form
|
||||
submission, and regenerate a slightly different form for the user. (Or
|
||||
you could treat it as a full-blown form submission, if the only widget
|
||||
of interest is the OptionSelectWidget.)</p>
|
||||
<p>For example, if you're asking a user for their address, some of the
|
||||
details will vary depending on which country they're in. You might make
|
||||
the country widget an OptionSelectWidget: if the user selects "Canada",
|
||||
you'll ask them for a province and a postal code; if they select "United
|
||||
States", you ask for a state and a zip code; and so forth. (I don't
|
||||
really recommend a user interface that works this way: you'll spend way
|
||||
too much time getting the details right ["How many states does Australia
|
||||
have again?"], and you're bound to get something wrong -- there are over
|
||||
200 countries in the world, after all.)</p>
|
||||
<p>Be warned that since OptionSelectWidget relies on Javascript to work,
|
||||
using it makes immediately makes your application less portable and more
|
||||
fragile. One thing to avoid: form elements with a name of <tt class="literal"><span class="pre">submit</span></tt>,
|
||||
since that masks the Javascript function called by OptionSelectWidget.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
315
doc/wsgi.html
315
doc/wsgi.html
|
@ -1,315 +0,0 @@
|
|||
<?xml version="1.0" encoding="us-ascii" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
|
||||
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
|
||||
<title>WSGI and Quixote</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger
|
||||
:Contact: goodger@users.sourceforge.net
|
||||
:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
|
||||
:Revision: $Revision: 4224 $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
|
||||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin-left: 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left {
|
||||
clear: left }
|
||||
|
||||
img.align-right {
|
||||
clear: right }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font-family: serif ;
|
||||
font-size: 100% }
|
||||
|
||||
pre.literal-block, pre.doctest-block {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em ;
|
||||
background-color: #eeeeee }
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
tt.docutils {
|
||||
background-color: #eeeeee }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="wsgi-and-quixote">
|
||||
<h1 class="title">WSGI and Quixote</h1>
|
||||
<p>"WSGI" stands for the Python Web Server Gateway Interface, defined in
|
||||
<a class="reference" href="http://www.python.org/dev/peps/pep-0333/">PEP 333</a>.</p>
|
||||
<p>The function <tt class="docutils literal"><span class="pre">quixote.get_wsgi_app()</span></tt> returns a WSGI application
|
||||
object for the current publisher instance.</p>
|
||||
<p>This WSGI application ties into the publisher at the <tt class="docutils literal"><span class="pre">process_request</span></tt>
|
||||
method, and bypasses the <tt class="docutils literal"><span class="pre">process</span></tt> method completely.</p>
|
||||
<div class="section">
|
||||
<h1><a id="an-example" name="an-example">An Example</a></h1>
|
||||
<p>For example, you can use the <tt class="docutils literal"><span class="pre">wsgiref</span></tt> module (included with
|
||||
Python 2.5 and later) to serve a Quixote application.</p>
|
||||
<pre class="literal-block">
|
||||
your_app.create_publisher()
|
||||
wsgi_app = quixote.get_wsgi_app()
|
||||
|
||||
from wsgiref.simple_server import make_server
|
||||
make_server('', 8000, wsgi_app)
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue