Update documentation for 2.8 changes.
Mention the export() decorator. Simplify the explaination of the mini demo. Suggest starting the server using python -m <module> since that avoids platform dependant paths. Explain how to make simple_server listen on a public interface. Include the latest docutils stylesheet.
This commit is contained in:
parent
97cb372be8
commit
b799be7d7f
|
@ -6,7 +6,7 @@ TXT_FILES = $(wildcard *.txt)
|
|||
HTML_FILES = $(filter-out LICENSE_24% CHANGES_24%,$(TXT_FILES:%.txt=%.html))
|
||||
|
||||
RST2HTML = rst2html.py
|
||||
RST2HTML_OPTS = -o us-ascii
|
||||
RST2HTML_OPTS = -o us-ascii --link-stylesheet --stylesheet-path=default.css
|
||||
|
||||
DEST_HOST = staging.mems-exchange.org
|
||||
DEST_DIR = /www/www-docroot/software/quixote/doc
|
||||
|
|
|
@ -4,7 +4,7 @@ Just overrides what I don't like about the standard docutils
|
|||
stylesheet.
|
||||
*/
|
||||
|
||||
@import url(/misc/docutils.css);
|
||||
@import url(docutils.css);
|
||||
|
||||
pre.literal-block, pre.doctest-block {
|
||||
margin-left: 1em ;
|
||||
|
|
156
doc/demo.txt
156
doc/demo.txt
|
@ -10,103 +10,87 @@ 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 http://localhost:8080.
|
||||
is as follows: in a terminal window, run::
|
||||
|
||||
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:
|
||||
python -m quixote.server.simple_server
|
||||
|
||||
simple_server.py --factory quixote.demo.mini_demo.create_publisher
|
||||
and in a browser, open http://localhost:8080. If you wish to run the
|
||||
demo on a remote computer, you will need to ask the server to listen
|
||||
on a public network interface. Usually::
|
||||
|
||||
or
|
||||
python -m quixote.server.simple_server --host=0
|
||||
|
||||
simple_server.py --factory quixote.demo.altdemo.create_publisher
|
||||
In your browser, replace `localhost` with the name of the server.
|
||||
|
||||
The simple_server.py script prints a usage message if you run it with a
|
||||
``--help`` command line argument. You can run different Quixote
|
||||
applications by using the ``--factory`` option to identify a callable
|
||||
that creates the publisher you want to use. In particular, you might
|
||||
try these demos::
|
||||
|
||||
python -m quixote.server.simple_server \
|
||||
--factory quixote.demo.mini_demo.create_publisher
|
||||
|
||||
or::
|
||||
|
||||
python -m quixote.server.simple_server \
|
||||
--factory quixote.demo.altdemo.create_publisher
|
||||
|
||||
|
||||
|
||||
Understanding the mini_demo
|
||||
---------------------------
|
||||
|
||||
Start the mini demo by running the command:
|
||||
simple_server.py --factory quixote.demo.mini_demo.create_publisher
|
||||
Start the mini demo by running the command::
|
||||
|
||||
python -m quixote.server.simple_server \
|
||||
--factory quixote.demo.mini_demo.create_publisher
|
||||
|
||||
In a browser, load http://localhost:8080. 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
|
||||
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.
|
||||
|
||||
Look at the source code in demo/mini_demo.py. Near the bottom you
|
||||
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.
|
||||
instance of the RootDirectory class defined just above. Each Quixote
|
||||
application has one Publisher instance. When a request arrives, the
|
||||
Publisher traverses the path of the URL, using the RootDirectory object.
|
||||
|
||||
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', ''].
|
||||
For the URL http://localhost:8080, Quixote traverses the path list::
|
||||
|
||||
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.
|
||||
['']
|
||||
|
||||
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
|
||||
http://localhost:8080/
|
||||
This path results in the function exported using the empty string as
|
||||
its name being called. The return value is send back to the browser,
|
||||
after adding some basic HTTP headers.
|
||||
|
||||
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'.
|
||||
To prevent potentially sensitive internal parts of your application are
|
||||
not exposed as callable URLs, Quixote requires that each Directory
|
||||
component must be explictly exported. Internally this is done using the
|
||||
_q_exports attribute of Directory objects. For convenience, a decorator
|
||||
function called 'export' is made available which takes care of updating
|
||||
the _q_exports list.
|
||||
|
||||
When you click on the link on the top page, you get
|
||||
http://localhost:8080/hello. 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.
|
||||
The 'export' decorator takes a single, optional keyword argument 'name'.
|
||||
If not provided, the exported name defaults to the name of the function.
|
||||
|
||||
Feeling bold? (Just kidding, this won't hurt at all.) Try opening
|
||||
http://localhost:8080/bogus. 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
|
||||
http://localhost:8080/hello 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.
|
||||
The mini demo contains only a single page besides the root page. The
|
||||
URL is http://localhost:8080/hello. Quixote traverses the path list::
|
||||
|
||||
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.
|
||||
['hello']
|
||||
|
||||
and finds the ``hello`` method of the ``RootDirectory`` object. It
|
||||
calls the method and returns the result as page.
|
||||
|
||||
|
||||
Understanding the root demo
|
||||
---------------------------
|
||||
|
||||
Start the root demo by running the command:
|
||||
simple_server.py --factory quixote.demo.create_publisher
|
||||
Start the root demo by running the command::
|
||||
|
||||
python -m quixote.server.simple_server
|
||||
|
||||
In a browser, open http://localhost:8080 as before.
|
||||
Click around at will.
|
||||
|
@ -124,35 +108,27 @@ 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.
|
||||
file.
|
||||
|
||||
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.
|
||||
that it has a index() method and it is exported with the name '' (i.e.
|
||||
the empty string).
|
||||
|
||||
Looking at the RootDirectoryMethods, including plain(), css() and
|
||||
favon_ico(), you will see examples where, in addition to returning a
|
||||
favicon_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
|
||||
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.
|
||||
|
||||
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.
|
||||
module. Note that 'extras' explicitly 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 index() method.
|
||||
|
||||
The _q_lookup() method
|
||||
----------------------
|
||||
|
@ -167,7 +143,7 @@ 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.
|
||||
a callable or a string.
|
||||
|
||||
In this particular case, the ExtrasDirectory._q_lookup() call returns
|
||||
an instance of IntegerUI (a subclass of Directory). The interest
|
||||
|
@ -176,7 +152,7 @@ on-the-fly during the traversal, especially for this particular
|
|||
component. Try loading http://localhost:8080/extras/12/ to see how
|
||||
this behaves.
|
||||
|
||||
Note that the correct URL to get to the IntegerUI(12)._q_index() call
|
||||
Note that the correct URL to get to the IntegerUI(12).index() call
|
||||
ends with a '/'. This can sometimes be confusing to people who expect
|
||||
http://localhost:8080/extras/12 to yield the same page as
|
||||
http://localhost:8080/extras/12/. If given the path ['extras', '12'],
|
||||
|
@ -216,4 +192,4 @@ Forms
|
|||
You can't get very far writing web applications without writing forms.
|
||||
The root demo includes, at http://localhost:8080/extras/form, a page
|
||||
that demonstrates basic usage of the Form class and widgets defined in
|
||||
the quixote.form package.
|
||||
the quixote.form package.
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 5951 2009-05-18 18:03:10Z milde $
|
||||
: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: 0 0 0.5em 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, .figure.align-left{
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: left }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
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: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
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% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
|
@ -100,7 +100,7 @@ it handles; we have split that line up here to make it easier to read::
|
|||
|
||||
127.0.0.1 - 2001-10-15 09:48:43
|
||||
2504 "GET /catalog/ HTTP/1.1"
|
||||
200 'Opera/6.0 (Linux; U)' 0.10sec
|
||||
200 'Opera/6.0 (Linux; U)' 0.100s
|
||||
|
||||
This line consists of:
|
||||
|
||||
|
|
Loading…
Reference in New Issue