Reorganize source

Added some simple WSGI documentation; cleaned up test code a bit.
Added test server status check.  Moved the quixote package into the
quixote/ subdirectory.  Added nose+twill tests under tests/.
This commit is contained in:
C. Titus Brown 2007-02-09 17:00:37 -06:00 committed by Neil Schemenauer
parent b05e30e2cc
commit a73c2957be
66 changed files with 556 additions and 5 deletions

View File

@ -4,6 +4,13 @@ Summary of changes
2.5a2 (released 2006-MM-DD)
-------------------------
C. Titus Brown <titus@idyll.org>:
Added some simple WSGI documentation; cleaned up test code a bit.
C. Titus Brown <titus@idyll.org>:
Moved package contents into quixote/ subdirectory; added tests/
subdirectory with some minimal tests.
C. Titus Brown <titus@idyll.org>:
Added quixote.html.use_qpy to switch Quixote over to using qpy
instead of htmltext. (Code contributed by Mike Orr.)

View File

@ -5,7 +5,7 @@
TXT_FILES = $(wildcard *.txt)
HTML_FILES = $(filter-out LICENSE_24% CHANGES_24%,$(TXT_FILES:%.txt=%.html))
RST2HTML = /www/python/bin/rst2html
RST2HTML = rst2html.py
RST2HTML_OPTS = -o us-ascii
DEST_HOST = staging.mems-exchange.org

315
doc/wsgi.html Normal file
View File

@ -0,0 +1,315 @@
<?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>&quot;WSGI&quot; 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>

23
doc/wsgi.txt Normal file
View File

@ -0,0 +1,23 @@
WSGI and Quixote
================
"WSGI" stands for the Python Web Server Gateway Interface, defined in
`PEP 333 <http://www.python.org/dev/peps/pep-0333/>`__.
The function ``quixote.get_wsgi_app()`` returns a WSGI application
object for the current publisher instance.
This WSGI application ties into the publisher at the ``process_request``
method, and bypasses the ``process`` method completely.
An Example
----------
For example, you can use the ``wsgiref`` module (included with
Python 2.5 and later) to serve a Quixote application. ::
your_app.create_publisher()
wsgi_app = quixote.get_wsgi_app()
from wsgiref.simple_server import make_server
make_server('', 8000, wsgi_app)

View File

@ -1,4 +1,9 @@
#!/usr/bin/env python
#try:
# from setuptools import setup
#except ImportError:
# print '(WARNING: importing distutils, not setuptools!)'
# from distutils.core import setup
# Setup script for Quixote
@ -6,7 +11,7 @@ import sys
import os
from distutils import core
from distutils.extension import Extension
from ptl.qx_distutils import qx_build_py
from quixote.ptl.qx_distutils import qx_build_py
VERSION = '2.5a1'
@ -24,11 +29,11 @@ if 'sdist' in sys.argv[1:]:
# a fast htmltext type
htmltext = Extension(name="quixote.html._c_htmltext",
sources=["html/_c_htmltext.c"])
sources=["quixote/html/_c_htmltext.c"])
# faster import hook for PTL modules
cimport = Extension(name="quixote.ptl.cimport",
sources=["ptl/cimport.c"])
sources=["quixote/ptl/cimport.c"])
kw = {'name': "Quixote",
'version': VERSION,
@ -38,7 +43,7 @@ kw = {'name': "Quixote",
'url': "http://www.quixote.ca/",
'license': "DFSG approved open source (see LICENSE.txt)",
'package_dir': {'quixote': os.curdir},
'package_dir': {'quixote': 'quixote'},
'packages': ['quixote', 'quixote.demo', 'quixote.form',
'quixote.html', 'quixote.ptl',
'quixote.server'],
@ -46,6 +51,8 @@ kw = {'name': "Quixote",
'ext_modules': [],
'cmdclass': {'build_py': qx_build_py},
# 'test_suite' : 'nose.collector'
}

15
tests/README.txt Normal file
View File

@ -0,0 +1,15 @@
These are tests for developers of Quixote 2.x.
To run them, you will need to install 'nose' and 'twill':
easy_install nose
easy_install http://darcs.idyll.org/~t/projects/twill-latest.tar.gz
Then, in the Quixote directory, run 'python setup.py build'. Put the
resulting build library in your PYTHONPATH, e.g.
export PYTHONPATH=/path/to/quixote/build/lib.linux-i686-2.4/
Finally, run 'nosetests' in the top-level Quixote directory, i.e. the directory
containing CHANGES.txt.

17
tests/__init__.py Normal file
View File

@ -0,0 +1,17 @@
import qx_testlib
import qx_testserver
import twill
url = None
def setup(package):
qx_testlib.cd_testdir()
package.url = qx_testlib.run_server(qx_testserver.create_publisher)
def teardown(package):
qx_testlib.kill_server()
qx_testlib.pop_testdir()
def test():
twill.commands.go(url)
twill.commands.find('hello, world')

81
tests/qx_testlib.py Normal file
View File

@ -0,0 +1,81 @@
import sys, subprocess
import quixote
from quixote.server.simple_server import run
from cStringIO import StringIO
import os
import socket
import urllib
_server_url = None
testdir = os.path.dirname(__file__)
print 'testdir is:', testdir
sys.path.insert(0, os.path.abspath(os.path.join(testdir, '..')))
import twill
def cd_testdir():
global cwd
cwd = os.getcwd()
os.chdir(testdir)
def pop_testdir():
global cwd
os.chdir(cwd)
def execute_twill_script(filename, inp=None, initial_url=None):
global testdir
if inp:
inp_fp = StringIO(inp)
old, sys.stdin = sys.stdin, inp_fp
scriptfile = os.path.join(testdir, filename)
try:
twill.execute_file(filename, initial_url=initial_url)
finally:
if inp:
sys.stdin = old
def run_server(create_fn, PORT=None):
"""
Run a Quixote simple_server on localhost:PORT with subprocess.
All output is captured & thrown away.
The parent process returns the URL on which the server is running.
"""
import time, tempfile
global _server_url
if PORT is None:
PORT = int(os.environ.get('QX_TEST_PORT', '8080'))
outfd = tempfile.mkstemp('quixote_tst')[0]
print 'STARTING:', sys.executable, 'tests/qx_testserver.py', os.getcwd()
process = subprocess.Popen([sys.executable, '-u', 'qx_testserver.py'],
stderr=subprocess.STDOUT,
stdout=outfd)
time.sleep(1)
result = process.poll()
if result is not None:
raise Exception("server is not running: return code %s" % (result,))
_server_url = 'http://localhost:%d/' % (PORT,)
return _server_url
def kill_server():
"""
Kill the previously started Quixote server.
"""
global _server_url
if _server_url != None:
try:
fp = urllib.urlopen('%sexit' % (_server_url,))
except:
pass
_server_url = None

35
tests/qx_testserver.py Normal file
View File

@ -0,0 +1,35 @@
"""
A simple test server for testing Quixote functionality.
"""
import os
import quixote
assert quixote.__version__ == '2.5a1'
from quixote.publish import Publisher
from quixote.directory import Directory
def create_publisher():
"Create & return a test publisher entry"
p = Publisher(TestServer())
p.is_thread_safe = True
return p
class TestServer(Directory):
_q_exports = ['', 'exit']
def _q_index(self):
return "hello, world"
def exit(self):
raise SystemExit
if __name__ == '__main__':
from quixote.server.simple_server import run
port = int(os.environ.get('QX_TEST_PORT', '8080'))
print 'starting qx_testserver on port %d.' % (port,)
try:
run(create_publisher, port=port)
except KeyboardInterrupt:
pass

8
tests/serve-via-wsgi.py Normal file
View File

@ -0,0 +1,8 @@
import quixote, qx_testserver
from wsgiref.simple_server import make_server
qx_testserver.create_publisher()
wsgi_app = quixote.get_wsgi_app()
httpd = make_server('', 8000, wsgi_app)
httpd.serve_forever()

17
tests/test-qpy.py Normal file
View File

@ -0,0 +1,17 @@
import quixote.html
try:
import qpy
except ImportError:
qpy = None
if qpy:
def setup():
quixote.html.use_qpy()
def test():
import quixote
assert quixote.html.htmltext == qpy.h8
def teardown():
quixote.html.cleanup_qpy()

26
tests/test-wsgi.py Normal file
View File

@ -0,0 +1,26 @@
import sys
import twill
import quixote
from qx_testserver import create_publisher
class TestWSGI:
def setup(self):
wsgi_app = None
x = sys.stdout # Quixote mangles sys.stdout; save.
try:
publisher = create_publisher()
wsgi_app = quixote.get_wsgi_app()
finally:
sys.stdout = x # restore.
twill.add_wsgi_intercept('localhost', 80, lambda:wsgi_app, '/qx_test')
def teardown(self):
twill.remove_wsgi_intercept('localhost', 80)
quixote.cleanup()
def test(self):
twill.commands.go('http://localhost:80/qx_test/')
twill.commands.find('hello, world')