├── .gitignore ├── ACKS ├── CHANGES ├── LICENSE ├── MANIFEST ├── MANIFEST.in ├── PKG-INFO ├── README ├── TODO ├── doc ├── INSTALL.html ├── INSTALL.txt ├── Makefile ├── PTL.html ├── PTL.txt ├── ZPL.txt ├── default.css ├── demo.html ├── demo.txt ├── form2conversion.html ├── form2conversion.txt ├── multi-threaded.html ├── multi-threaded.txt ├── programming.html ├── programming.txt ├── session-mgmt.html ├── session-mgmt.txt ├── static-files.html ├── static-files.txt ├── ua_test.py ├── upgrading.html ├── upgrading.txt ├── upload.html ├── upload.txt ├── utest_html.py ├── web-server.html ├── web-server.txt ├── web-services.html ├── web-services.txt ├── widgets.html └── widgets.txt ├── quixote ├── __init__.py ├── _py_htmltext.py ├── config.py ├── demo │ ├── __init__.py │ ├── demo.cgi │ ├── demo.conf │ ├── demo_scgi.py │ ├── demo_scgi.sh │ ├── forms.ptl │ ├── integer_ui.py │ ├── pages.ptl │ ├── run_cgi.py │ ├── session.ptl │ ├── session_demo.cgi │ ├── upload.cgi │ └── widgets.ptl ├── errors.py ├── fcgi.py ├── form │ ├── __init__.py │ ├── form.py │ └── widget.py ├── form2 │ ├── __init__.py │ ├── compatibility.py │ ├── css.py │ ├── form.py │ └── widget.py ├── html.py ├── http_request.py ├── http_response.py ├── ihooks.py ├── mod_python_handler.py ├── ptl_compile.py ├── ptl_import.py ├── ptlc_dump.py ├── publish.py ├── qwip.py ├── qx_distutils.py ├── sendmail.py ├── server │ ├── __init__.py │ ├── medusa_http.py │ └── twisted_http.py ├── session.py ├── upload.py └── util.py ├── setup.py ├── src ├── Makefile ├── _c_htmltext.c ├── cimport.c └── setup.py └── test ├── __init__.py ├── base.py ├── test_config.py ├── test_drop_dup_value.py ├── test_json_request.py ├── test_qwip.py └── test_upload.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info/* 2 | *.pyc 3 | *~ 4 | build/* 5 | dist/* 6 | -------------------------------------------------------------------------------- /ACKS: -------------------------------------------------------------------------------- 1 | Acknowledgements 2 | ================ 3 | 4 | The Quixote developer team would like to thank everybody who 5 | contributed in any way, with code, hints, bug reports, ideas, moral 6 | support, endorsement, or even complaints. Listed in alphabetical 7 | order: 8 | 9 | David Ascher 10 | Anton Benard 11 | Titus Brown 12 | Oleg Broytmann 13 | David M. Cooke 14 | Jonathan Corbet 15 | Herman Cuppens 16 | Toby Dickenson 17 | Ray Drew 18 | Jim Dukarm 19 | Quinn Dunkan 20 | Robin Dunn (original author of fcgi.py) 21 | Jon Dyte 22 | David Edwards 23 | Graham Fawcett 24 | Jim Fulton (original author of the *Request and *Response classes) 25 | David Goodger 26 | Neal M. Holtz 27 | Kaweh Kazemi 28 | Shahms E. King 29 | A.M. Kuchling 30 | Erno Kuusela 31 | Nicola Larosa 32 | Hamish Lawson 33 | Patrick K. O'Brien 34 | Brendan T O'Connor 35 | Ed Overly 36 | Paul Richardson 37 | Jeff Rush 38 | Neil Schemenauer 39 | Jason Sibre 40 | Gregory P. Smith 41 | Mikhail Sobolev 42 | Johann Visagie 43 | Greg Ward 44 | The whole gang at the Zope Corporation 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CNRI OPEN SOURCE LICENSE AGREEMENT 2 | 3 | IMPORTANT: PLEASE READ THE FOLLOWING AGREEMENT CAREFULLY. BY 4 | COPYING, INSTALLING OR OTHERWISE USING QUIXOTE-1.2 SOFTWARE, YOU 5 | ARE DEEMED TO HAVE AGREED TO THE TERMS AND CONDITIONS OF THIS 6 | LICENSE AGREEMENT. 7 | 8 | 1. This LICENSE AGREEMENT is between Corporation for National 9 | Research Initiatives, having an office at 1895 Preston White 10 | Drive, Reston, VA 20191 ("CNRI"), and the Individual or 11 | Organization ("Licensee") copying, installing or otherwise using 12 | Quixote-1.2 software in source or binary form and its associated 13 | documentation ("Quixote-1.2"). 14 | 15 | 2. Subject to the terms and conditions of this License Agreement, 16 | CNRI hereby grants Licensee a nonexclusive, royalty-free, world- 17 | wide license to reproduce, analyze, test, perform and/or display 18 | publicly, prepare derivative works, distribute, and otherwise use 19 | Quixote-1.2 alone or in any derivative version, provided, 20 | however, that CNRI's License Agreement and CNRI's notice of 21 | copyright, i.e., "Copyright (c) 2004 Corporation for National 22 | Research Initiatives; All Rights Reserved" are retained in 23 | Quixote-1.2 alone or in any derivative version prepared by 24 | Licensee. 25 | 26 | 3. In the event Licensee prepares a derivative work that is based on 27 | or incorporates Quixote-1.2 or any part thereof, and wants to 28 | make the derivative work available to others as provided herein, 29 | then Licensee hereby agrees to include in any such work a brief 30 | summary of the changes made to Quixote-1.2. 31 | 32 | 4. CNRI is making Quixote-1.2 available to Licensee on an "AS IS" 33 | basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 34 | IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO 35 | AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY 36 | OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF QUIXOTE- 37 | 1.2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 38 | 39 | 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF 40 | QUIXOTE-1.2 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES 41 | OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE 42 | USING QUIXOTE-1.2, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF 43 | THE POSSIBILITY THEREOF. 44 | 45 | 6. This License Agreement will automatically terminate upon a 46 | material breach of its terms and conditions. 47 | 48 | 7. This License Agreement shall be governed by and interpreted in 49 | all respects by the law of the State of Virginia, excluding 50 | Virginia's conflict of law provisions. Nothing in this License 51 | Agreement shall be deemed to create any relationship of agency, 52 | partnership, or joint venture between CNRI and Licensee. This 53 | License Agreement does not grant permission to use CNRI 54 | trademarks or trade name in a trademark sense to endorse or 55 | promote products or services of Licensee, or any third party. 56 | 57 | 8. By copying, installing or otherwise using Quixote-1.2, Licensee 58 | agrees to be bound by the terms and conditions of this License 59 | Agreement. 60 | 61 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | ACKS 2 | CHANGES 3 | LICENSE 4 | MANIFEST 5 | MANIFEST.in 6 | README 7 | TODO 8 | __init__.py 9 | _py_htmltext.py 10 | config.py 11 | errors.py 12 | fcgi.py 13 | html.py 14 | http_request.py 15 | http_response.py 16 | mod_python_handler.py 17 | ptl_compile.py 18 | ptl_import.py 19 | ptlc_dump.py 20 | publish.py 21 | qx_distutils.py 22 | sendmail.py 23 | session.py 24 | setup.py 25 | upload.py 26 | util.py 27 | ./__init__.py 28 | ./_py_htmltext.py 29 | ./config.py 30 | ./errors.py 31 | ./fcgi.py 32 | ./html.py 33 | ./http_request.py 34 | ./http_response.py 35 | ./mod_python_handler.py 36 | ./ptl_compile.py 37 | ./ptl_import.py 38 | ./ptlc_dump.py 39 | ./publish.py 40 | ./qx_distutils.py 41 | ./sendmail.py 42 | ./session.py 43 | ./upload.py 44 | ./util.py 45 | ./demo/__init__.py 46 | ./demo/demo_scgi.py 47 | ./demo/forms.ptl 48 | ./demo/integer_ui.py 49 | ./demo/pages.ptl 50 | ./demo/run_cgi.py 51 | ./demo/session.ptl 52 | ./demo/widgets.ptl 53 | ./form/__init__.py 54 | ./form/form.py 55 | ./form/widget.py 56 | ./form2/__init__.py 57 | ./form2/compatibility.py 58 | ./form2/css.py 59 | ./form2/form.py 60 | ./form2/widget.py 61 | ./server/__init__.py 62 | ./server/medusa_http.py 63 | ./server/twisted_http.py 64 | demo/__init__.py 65 | demo/demo.cgi 66 | demo/demo.conf 67 | demo/demo_scgi.py 68 | demo/demo_scgi.sh 69 | demo/forms.ptl 70 | demo/integer_ui.py 71 | demo/pages.ptl 72 | demo/run_cgi.py 73 | demo/session.ptl 74 | demo/session_demo.cgi 75 | demo/upload.cgi 76 | demo/widgets.ptl 77 | doc/INSTALL.html 78 | doc/INSTALL.txt 79 | doc/Makefile 80 | doc/PTL.html 81 | doc/PTL.txt 82 | doc/ZPL.txt 83 | doc/default.css 84 | doc/demo.html 85 | doc/demo.txt 86 | doc/form2conversion.html 87 | doc/form2conversion.txt 88 | doc/multi-threaded.html 89 | doc/multi-threaded.txt 90 | doc/programming.html 91 | doc/programming.txt 92 | doc/session-mgmt.html 93 | doc/session-mgmt.txt 94 | doc/static-files.html 95 | doc/static-files.txt 96 | doc/upgrading.html 97 | doc/upgrading.txt 98 | doc/upload.html 99 | doc/upload.txt 100 | doc/web-server.html 101 | doc/web-server.txt 102 | doc/web-services.html 103 | doc/web-services.txt 104 | doc/widgets.html 105 | doc/widgets.txt 106 | form/__init__.py 107 | form/form.py 108 | form/widget.py 109 | form2/__init__.py 110 | form2/compatibility.py 111 | form2/css.py 112 | form2/form.py 113 | form2/widget.py 114 | server/__init__.py 115 | server/medusa_http.py 116 | server/twisted_http.py 117 | src/Makefile 118 | src/_c_htmltext.c 119 | src/cimport.c 120 | src/setup.py 121 | test/__init__.py 122 | test/ua_test.py 123 | test/utest_html.py 124 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | global-include *.py *.ptl 2 | include README LICENSE MANIFEST.in MANIFEST CHANGES TODO ACKS 3 | include doc/*.txt doc/*.css doc/Makefile 4 | recursive-include doc *.html 5 | include demo/*.cgi demo/*.conf demo/*.sh 6 | include src/*.c src/Makefile 7 | -------------------------------------------------------------------------------- /PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: Quixote 3 | Version: 1.2 4 | Summary: A highly Pythonic Web application framework 5 | Home-page: http://www.mems-exchange.org/software/quixote/ 6 | Author: MEMS Exchange 7 | Author-email: quixote@mems-exchange.org 8 | License: CNRI Open Source License (see LICENSE.txt) 9 | Provides: quixote-1.2 10 | Provides: quixote.demo-1.2 11 | Provides: quixote.form-1.2 12 | Provides: quixote.form2-1.2 13 | Provides: quixote.server-1.2 14 | Download-URL: http://www.mems-exchange.org/software/files/quixote/Quixote-1.2.tar.gz 15 | Description: UNKNOWN 16 | Platform: UNKNOWN 17 | Classifier: Development Status :: 5 - Production/Stable 18 | Classifier: Environment :: Web Environment 19 | Classifier: License :: OSI Approved :: Python License (CNRI Python License) 20 | Classifier: Intended Audience :: Developers 21 | Classifier: Operating System :: Unix 22 | Classifier: Operating System :: Microsoft :: Windows 23 | Classifier: Operating System :: MacOS :: MacOS X 24 | Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content 25 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Quixote 2 | ======= 3 | 4 | Quixote is yet another framework for developing Web applications in 5 | Python. The design goals were: 6 | 7 | 1) To allow easy development of Web applications where the 8 | emphasis is more on complicated programming logic than 9 | complicated templating. 10 | 11 | 2) To make the templating language as similar to Python as possible, 12 | in both syntax and semantics. The aim is to make as many of the 13 | skills and structural techniques used in writing regular Python 14 | code applicable to Web applications built using Quixote. 15 | 16 | 3) No magic. When it's not obvious what to do in 17 | a certain case, Quixote refuses to guess. 18 | 19 | If you view a web site as a program, and web pages as subroutines, 20 | Quixote just might be the tool for you. If you view a web site as a 21 | graphic design showcase, and each web page as an individual work of art, 22 | Quixote is probably not what you're looking for. 23 | 24 | An additional requirement was that the entire system had to be 25 | implementable in a week or two. The initial version of Quixote was 26 | indeed cranked out in about that time -- thank you, Python! 27 | 28 | We've tried to reuse as much existing code as possible: 29 | 30 | * The HTTPRequest and HTTPResponse classes are distantly 31 | derived from their namesakes in Zope, but we've removed 32 | huge amounts of Zope-specific code. 33 | 34 | * The quixote.fcgi module is derived from Robin Dunn's FastCGI module, 35 | available at 36 | http://alldunn.com/python/#fcgi 37 | 38 | Quixote requires Python 2.1 or greater to run. We only test Quixote 39 | with Python 2.3, but it should still work with 2.1 and 2.2. 40 | 41 | For installation instructions, see the doc/INSTALL.txt file (or 42 | http://www.mems-exchange.org/software/quixote/doc/INSTALL.html). 43 | 44 | If you're switching to a newer version of Quixote from an older 45 | version, please refer to doc/upgrading.txt for explanations of any 46 | backward-incompatible changes. 47 | 48 | 49 | Overview 50 | ======== 51 | 52 | Quixote works by using a Python package to store all the code and HTML 53 | for a Web-based application. There's a simple framework for 54 | publishing code and objects on the Web, and the publishing loop can be 55 | customized by subclassing the Publisher class. You can think of it as 56 | a toolkit to build your own smaller, simpler version of Zope, 57 | specialized for your application. 58 | 59 | An application using Quixote is a Python package containing .py and 60 | .ptl files. 61 | 62 | webapp/ # Root of package 63 | __init__.py 64 | module1.py 65 | module2.py 66 | pages1.ptl 67 | pages2.ptl 68 | 69 | PTL, the Python Template Language, is used to mix HTML with Python code. 70 | More importantly, Python can be used to drive the generation of HTML. 71 | An import hook is defined so that PTL files can be imported just like 72 | Python modules. The basic syntax of PTL is Python's, with a few small 73 | changes: 74 | 75 | def plain [text] barebones_header(title=None, 76 | description=None): 77 | """ 78 | 79 | %s 80 | """ % html_quote(str(title)) 81 | if description: 82 | '' % html_quote(description) 83 | 84 | '' 85 | 86 | See doc/PTL.txt for a detailed explanation of PTL. 87 | 88 | 89 | Quick start 90 | =========== 91 | 92 | For instant gratification, see doc/demo.txt. This explains how to get 93 | the Quixote demo up and running, so you can play with Quixote without 94 | actually having to write any code. 95 | 96 | 97 | Documentation 98 | ============= 99 | 100 | All the documentation is in the doc/ subdirectory, in both text and 101 | HTML. Or you can browse it online from 102 | http://www.mems-exchange.org/software/quixote/doc/ 103 | 104 | Recommended reading: 105 | 106 | demo.txt getting the Quixote demo up and running, and 107 | how the demo works 108 | programming.txt the components of a Quixote application: how 109 | to write your own Quixote apps 110 | PTL.txt the Python Template Language, used by Quixote 111 | apps to generate web pages 112 | web-server.txt how to configure your web server for Quixote 113 | 114 | Optional reading (more advanced or arcane stuff): 115 | 116 | session-mgmt.txt session management: how to track information 117 | across requests 118 | static-files.txt making static files and CGI scripts available 119 | upload.txt how to handle HTTP uploads with Quixote 120 | upgrading.txt info on backward-incompatible changes that may 121 | affect applications written with earlier versions 122 | widgets.txt reference documentation for the Quixote Widget 123 | classes (which underly the form library) 124 | web-services.txt how to write web services using Quixote and 125 | XML-RPC 126 | 127 | 128 | Authors, copyright, and license 129 | =============================== 130 | 131 | Copyright (c) 2000-2003 CNRI. 132 | 133 | Quixote was primarily written by Andrew Kuchling, Neil Schemenauer, and 134 | Greg Ward. 135 | 136 | Overall, Quixote is covered by the CNRI Open Source License Agreement; 137 | see LICENSE for details. 138 | 139 | Portions of Quixote are derived from Zope, and are also covered by the 140 | ZPL (Zope Public License); see ZPL.txt. 141 | 142 | Full acknowledgments are in the ACKS file. 143 | 144 | 145 | Availability, home page, and mailing lists 146 | ========================================== 147 | 148 | The Quixote home page is: 149 | http://www.mems-exchange.org/software/quixote/ 150 | 151 | You'll find the latest stable release there. The current development 152 | code is also available via CVS; for instructions, see 153 | http://www.mems-exchange.org/software/quixote/cvs.html 154 | 155 | Discussion of Quixote occurs on the quixote-users mailing list: 156 | http://mail.mems-exchange.org/mailman/listinfo/quixote-users/ 157 | 158 | To follow development at the most detailed level by seeing every CVS 159 | checkin, join the quixote-checkins mailing list: 160 | http://mail.mems-exchange.org/mailman/listinfo/quixote-checkins/ 161 | 162 | 163 | -- 164 | A.M. Kuchling 165 | Neil Schemenauer 166 | Greg Ward 167 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/douban/douban-quixote/20cda948580802ee4205b72fe46728de9dda5253/TODO -------------------------------------------------------------------------------- /doc/INSTALL.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Installing Quixote 8 | 9 | 10 | 11 |
12 |

Installing Quixote

13 |
14 |

Best-case scenario

15 |

If you are using Python 2.2 or later, and have never installed Quixote 16 | with your current version of Python, you're in luck. Just run

17 |
18 | python setup.py install
19 |

and you're done. Proceed to the demo documentation to learn how to get 20 | Quixote working.

21 |

If you're using an older Python, or if you're upgrading from an older 22 | Quixote version, read on.

23 |
24 |
25 |

Upgrading from an older Quixote version

26 |

We strongly recommend that you remove any old Quixote version before 27 | installing a new one. First, find out where your old Quixote 28 | installation is:

29 |
30 | python -c "import os, quixote; print os.path.dirname(quixote.__file__)"
31 |

and then remove away the reported directory. (If the import fails, then 32 | you don't have an existing Quixote installation.)

33 |

Then proceed as above:

34 |
35 | python setup.py install
36 |
37 |
38 |

Using Quixote with Python 2.0 or 2.1

39 |

If you are using Python 2.0 or 2.1 then you need to install the 40 | compiler package from the Python source distribution. The 41 | compiler package is for parsing Python source code and generating 42 | Python bytecode, and the PTL compiler is built on top of it. With 43 | Python 2.0 and 2.1, this package was included in Python's source 44 | distribution, but not installed as part of the standard library.

45 |

Assuming your Python source distribution is in /tmp/Python-2.1.2:

46 |
47 | cd /tmp/Python-2.1.2/Tools/compiler
48 | python setup.py install
49 | 
50 |

(Obviously, you'll have to adjust this to reflect your Python version 51 | and where you kept the source distribution after installing Python.)

52 |
53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /doc/INSTALL.txt: -------------------------------------------------------------------------------- 1 | Installing Quixote 2 | ================== 3 | 4 | Best-case scenario 5 | ------------------ 6 | 7 | If you are using Python 2.2 or later, and have never installed Quixote 8 | with your current version of Python, you're in luck. Just run 9 | 10 | python setup.py install 11 | 12 | and you're done. Proceed to the demo documentation to learn how to get 13 | Quixote working. 14 | 15 | If you're using an older Python, or if you're upgrading from an older 16 | Quixote version, read on. 17 | 18 | 19 | Upgrading from an older Quixote version 20 | --------------------------------------- 21 | 22 | We strongly recommend that you remove any old Quixote version before 23 | installing a new one. First, find out where your old Quixote 24 | installation is: 25 | 26 | python -c "import os, quixote; print os.path.dirname(quixote.__file__)" 27 | 28 | and then remove away the reported directory. (If the import fails, then 29 | you don't have an existing Quixote installation.) 30 | 31 | Then proceed as above: 32 | 33 | python setup.py install 34 | 35 | 36 | Using Quixote with Python 2.0 or 2.1 37 | ------------------------------------ 38 | 39 | If you are using Python 2.0 or 2.1 then you need to install the 40 | ``compiler`` package from the Python source distribution. The 41 | ``compiler`` package is for parsing Python source code and generating 42 | Python bytecode, and the PTL compiler is built on top of it. With 43 | Python 2.0 and 2.1, this package was included in Python's source 44 | distribution, but not installed as part of the standard library. 45 | 46 | Assuming your Python source distribution is in ``/tmp/Python-2.1.2``:: 47 | 48 | cd /tmp/Python-2.1.2/Tools/compiler 49 | python setup.py install 50 | 51 | (Obviously, you'll have to adjust this to reflect your Python version 52 | and where you kept the source distribution after installing Python.) 53 | 54 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile to convert Quixote docs to HTML 3 | # 4 | # $Id: Makefile 20217 2003-01-16 20:51:53Z akuchlin $ 5 | # 6 | 7 | TXT_FILES = $(wildcard *.txt) 8 | HTML_FILES = $(filter-out ZPL%,$(TXT_FILES:%.txt=%.html)) 9 | 10 | RST2HTML = /www/python/bin/rst2html 11 | RST2HTML_OPTS = -o us-ascii 12 | 13 | DEST_HOST = staging.mems-exchange.org 14 | DEST_DIR = /www/www-docroot/software/quixote/doc 15 | 16 | SS = default.css 17 | 18 | %.html: %.txt 19 | $(RST2HTML) $(RST2HTML_OPTS) $< $@ 20 | 21 | all: $(HTML_FILES) 22 | 23 | clean: 24 | rm -f $(HTML_FILES) 25 | 26 | install: 27 | rsync -vptgo *.html $(SS) $(DEST_HOST):$(DEST_DIR) 28 | 29 | local-install: 30 | dir=`pwd` ; \ 31 | cd $(DEST_DIR) && ln -sf $$dir/*.html $$dir/$(SS) . 32 | -------------------------------------------------------------------------------- /doc/PTL.txt: -------------------------------------------------------------------------------- 1 | PTL: Python Template Language 2 | ============================= 3 | 4 | Introduction 5 | ------------ 6 | 7 | PTL is the templating language used by Quixote. Most web templating 8 | languages embed a real programming language in HTML, but PTL inverts 9 | this model by merely tweaking Python to make it easier to generate 10 | HTML pages (or other forms of text). In other words, PTL is basically 11 | Python with a novel way to specify function return values. 12 | 13 | Specifically, a PTL template is designated by inserting a ``[plain]`` 14 | or ``[html]`` modifier after the function name. The value of 15 | expressions inside templates are kept, not discarded. If the type is 16 | ``[html]`` then non-literal strings are passed through a function that 17 | escapes HTML special characters. 18 | 19 | 20 | Plain text templates 21 | -------------------- 22 | 23 | Here's a sample plain text template:: 24 | 25 | def foo [plain] (x, y = 5): 26 | "This is a chunk of static text." 27 | greeting = "hello world" # statement, no PTL output 28 | print 'Input values:', x, y 29 | z = x + y 30 | """You can plug in variables like x (%s) 31 | in a variety of ways.""" % x 32 | 33 | "\n\n" 34 | "Whitespace is important in generated text.\n" 35 | "z = "; z 36 | ", but y is " 37 | y 38 | "." 39 | 40 | Obviously, templates can't have docstrings, but otherwise they follow 41 | Python's syntactic rules: indentation indicates scoping, single-quoted 42 | and triple-quoted strings can be used, the same rules for continuing 43 | lines apply, and so forth. PTL also follows all the expected semantics 44 | of normal Python code: so templates can have parameters, and the 45 | parameters can have default values, be treated as keyword arguments, 46 | etc. 47 | 48 | The difference between a template and a regular Python function is that 49 | inside a template the result of expressions are saved as the return 50 | value of that template. Look at the first part of the example again:: 51 | 52 | def foo [plain] (x, y = 5): 53 | "This is a chunk of static text." 54 | greeting = "hello world" # statement, no PTL output 55 | print 'Input values:', x, y 56 | z = x + y 57 | """You can plug in variables like x (%s) 58 | in a variety of ways.""" % x 59 | 60 | Calling this template with ``foo(1, 2)`` results in the following 61 | string:: 62 | 63 | This is a chunk of static text.You can plug in variables like x (1) 64 | in a variety of ways. 65 | 66 | Normally when Python evaluates expressions inside functions, it just 67 | discards their values, but in a ``[plain]`` PTL template the value is 68 | converted to a string using ``str()`` and appended to the template's 69 | return value. There's a single exception to this rule: ``None`` is the 70 | only value that's ever ignored, adding nothing to the output. (If this 71 | weren't the case, calling methods or functions that return ``None`` 72 | would require assigning their value to a variable. You'd have to write 73 | ``dummy = list.sort()`` in PTL code, which would be strange and 74 | confusing.) 75 | 76 | The initial string in a template isn't treated as a docstring, but is 77 | just incorporated in the generated output; therefore, templates can't 78 | have docstrings. No whitespace is ever automatically added to the 79 | output, resulting in ``...text.You can ...`` from the example. You'd 80 | have to add an extra space to one of the string literals to correct 81 | this. 82 | 83 | The assignment to the ``greeting`` local variable is a statement, not an 84 | expression, so it doesn't return a value and produces no output. The 85 | output from the ``print`` statement will be printed as usual, but won't 86 | go into the string generated by the template. Quixote directs standard 87 | output into Quixote's debugging log; if you're using PTL on its own, you 88 | should consider doing something similar. ``print`` should never be used 89 | to generate output returned to the browser, only for adding debugging 90 | traces to a template. 91 | 92 | Inside templates, you can use all of Python's control-flow statements:: 93 | 94 | def numbers [plain] (n): 95 | for i in range(n): 96 | i 97 | " " # PTL does not add any whitespace 98 | 99 | Calling ``numbers(5)`` will return the string ``"1 2 3 4 5 "``. You can 100 | also have conditional logic or exception blocks:: 101 | 102 | def international_hello [plain] (language): 103 | if language == "english": 104 | "hello" 105 | elif language == "french": 106 | "bonjour" 107 | else: 108 | raise ValueError, "I don't speak %s" % language 109 | 110 | 111 | HTML templates 112 | -------------- 113 | 114 | Since PTL is usually used to generate HTML documents, an ``[html]`` 115 | template type has been provided to make generating HTML easier. 116 | 117 | A common error when generating HTML is to grab data from the browser 118 | or from a database and incorporate the contents without escaping 119 | special characters such as '<' and '&'. This leads to a class of 120 | security bugs called "cross-site scripting" bugs, where a hostile user 121 | can insert arbitrary HTML in your site's output that can link to other 122 | sites or contain JavaScript code that does something nasty (say, 123 | popping up 10,000 browser windows). 124 | 125 | Such bugs occur because it's easy to forget to HTML-escape a string, 126 | and forgetting it in just one location is enough to open a hole. PTL 127 | offers a solution to this problem by being able to escape strings 128 | automatically when generating HTML output, at the cost of slightly 129 | diminished performance (a few percent). 130 | 131 | Here's how this feature works. PTL defines a class called 132 | ``htmltext`` that represents a string that's already been HTML-escaped 133 | and can be safely sent to the client. The function ``htmlescape(string)`` 134 | is used to escape data, and it always returns an ``htmltext`` 135 | instance. It does nothing if the argument is already ``htmltext``. 136 | Both ``htmltext`` and ``htmlescape`` are available from the global 137 | namespace in PTL modules. 138 | 139 | If a template function is declared ``[html]`` instead of ``[text]`` 140 | then two things happen. First, all literal strings in the function 141 | become instances of ``htmltext`` instead of Python's ``str``. Second, 142 | the values of expressions are passed through ``htmlescape()`` instead 143 | of ``str()``. 144 | 145 | ``htmltext`` type is like the ``str`` type except that operations 146 | combining strings and ``htmltext`` instances will result in the string 147 | being passed through ``htmlescape()``. For example:: 148 | 149 | >>> from quixote.html import htmltext 150 | >>> htmltext('a') + 'b' 151 | 152 | >>> 'a' + htmltext('b') 153 | 154 | >>> htmltext('a%s') % 'b' 155 | 156 | >>> response = 'green eggs & ham' 157 | >>> htmltext('The response was: %s') % response 158 | 159 | 160 | Note that calling ``str()`` strips the ``htmltext`` type and should be 161 | avoided since it usually results in characters being escaped more than 162 | once. While ``htmltext`` behaves much like a regular string, it is 163 | sometimes necessary to insert a ``str()`` inside a template in order 164 | to obtain a genuine string. For example, the ``re`` module requires 165 | genuine strings. We have found that explicit calls to ``str()`` can 166 | often be avoided by splitting some code out of the template into a 167 | helper function written in regular Python. 168 | 169 | It is also recommended that the ``htmltext`` constructor be used as 170 | sparingly as possible. The reason is that when using the htmltext 171 | feature of PTL, explicit calls to ``htmltext`` become the most likely 172 | source of cross-site scripting holes. Calling ``htmltext`` is like 173 | saying "I am absolutely sure this piece of data cannot contain malicious 174 | HTML code injected by a user. Don't escape HTML special characters 175 | because I want them." 176 | 177 | Note that literal strings in template functions declared with 178 | ``[html]`` are htmltext instances, and therefore won't be escaped. 179 | You'll only need to use ``htmltext`` when HTML markup comes from 180 | outside the template. For example, if you want to include a file 181 | containing HTML:: 182 | 183 | def output_file [html] (): 184 | '' # does not get escaped 185 | htmltext(open("myfile.html").read()) 186 | '' 187 | 188 | In the common case, templates won't be dealing with HTML markup from 189 | external sources, so you can write straightforward code. Consider 190 | this function to generate the contents of the ``HEAD`` element:: 191 | 192 | def meta_tags [html] (title, description): 193 | '%s' % title 194 | '\n' % description 195 | 196 | There are no calls to ``htmlescape()`` at all, but string literals 197 | such as ``%s`` have all be turned into ``htmltext`` 198 | instances, so the string variables will be automatically escaped:: 199 | 200 | >>> t.meta_tags('Catalog', 'A catalog of our cool products') 201 | Catalog 202 | \n'> 203 | >>> t.meta_tags('Dissertation on ', 204 | ... 'Discusses the "LINK" and "META" tags') 205 | Dissertation on <HEAD> 206 | \n'> 208 | >>> 209 | 210 | Note how the title and description have had HTML-escaping applied to them. 211 | (The output has been manually pretty-printed to be more readable.) 212 | 213 | Once you start using ``htmltext`` in one of your templates, mixing 214 | plain and HTML templates is tricky because of ``htmltext``'s automatic 215 | escaping; plain templates that generate HTML tags will be 216 | double-escaped. One approach is to just use HTML templates throughout 217 | your application. Alternatively you can use ``str()`` to convert 218 | ``htmltext`` instances to regular Python strings; just be sure the 219 | resulting string isn't HTML-escaped again. 220 | 221 | Two implementations of ``htmltext`` are provided, one written in pure 222 | Python and a second one implemented as a C extension. Both versions 223 | have seen production use. 224 | 225 | 226 | PTL modules 227 | ----------- 228 | 229 | PTL templates are kept in files with the extension .ptl. Like Python 230 | files, they are byte-compiled on import, and the byte-code is written to 231 | a compiled file with the extension ``.ptlc``. Since vanilla Python 232 | doesn't know anything about PTL, Quixote provides an import hook to let 233 | you import PTL files just like regular Python modules. The standard way 234 | to install this import hook is by calling the ``enable_ptl()`` function:: 235 | 236 | from quixote import enable_ptl 237 | enable_ptl() 238 | 239 | (Note: if you're using ZODB, always import ZODB *before* installing the 240 | PTL import hook. There's some interaction which causes importing the 241 | TimeStamp module to fail when the PTL import hook is installed; we 242 | haven't debugged the problem.) 243 | 244 | Once the import hook is installed, PTL files can be imported as if they 245 | were Python modules. If all the example templates shown here were put 246 | into a file named ``foo.ptl``, you could then write Python code that did 247 | this:: 248 | 249 | from foo import numbers 250 | def f(): 251 | return numbers(10) 252 | 253 | You may want to keep this little function in your ``PYTHONSTARTUP`` 254 | file:: 255 | 256 | def ptl(): 257 | try: 258 | import ZODB 259 | except ImportError: 260 | pass 261 | from quixote import enable_ptl 262 | enable_ptl() 263 | 264 | This is useful if you want to interactively play with a PTL module. 265 | 266 | 267 | $Id: PTL.txt 25237 2004-09-30 18:28:31Z nascheme $ 268 | -------------------------------------------------------------------------------- /doc/ZPL.txt: -------------------------------------------------------------------------------- 1 | Zope Public License (ZPL) Version 2.0 2 | ----------------------------------------------- 3 | 4 | This software is Copyright (c) Zope Corporation (tm) and 5 | Contributors. All rights reserved. 6 | 7 | This license has been certified as open source. It has also 8 | been designated as GPL compatible by the Free Software 9 | Foundation (FSF). 10 | 11 | Redistribution and use in source and binary forms, with or 12 | without modification, are permitted provided that the 13 | following conditions are met: 14 | 15 | 1. Redistributions in source code must retain the above 16 | copyright notice, this list of conditions, and the following 17 | disclaimer. 18 | 19 | 2. Redistributions in binary form must reproduce the above 20 | copyright notice, this list of conditions, and the following 21 | disclaimer in the documentation and/or other materials 22 | provided with the distribution. 23 | 24 | 3. The name Zope Corporation (tm) must not be used to 25 | endorse or promote products derived from this software 26 | without prior written permission from Zope Corporation. 27 | 28 | 4. The right to distribute this software or to use it for 29 | any purpose does not give you the right to use Servicemarks 30 | (sm) or Trademarks (tm) of Zope Corporation. Use of them is 31 | covered in a separate agreement (see 32 | http://www.zope.com/Marks). 33 | 34 | 5. If any files are modified, you must cause the modified 35 | files to carry prominent notices stating that you changed 36 | the files and the date of any change. 37 | 38 | Disclaimer 39 | 40 | THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' 41 | AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT 42 | NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 43 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 44 | NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE 45 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 46 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 48 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 49 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 50 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 51 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 52 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 53 | DAMAGE. 54 | 55 | 56 | This software consists of contributions made by Zope 57 | Corporation and many individuals on behalf of Zope 58 | Corporation. Specific attributions are listed in the 59 | accompanying credits file. 60 | -------------------------------------------------------------------------------- /doc/default.css: -------------------------------------------------------------------------------- 1 | /* 2 | Cascading style sheet for the Quixote documentation. 3 | Just overrides what I don't like about the standard docutils 4 | stylesheet. 5 | 6 | $Id: default.css 20217 2003-01-16 20:51:53Z akuchlin $ 7 | */ 8 | 9 | @import url(/misc/docutils.css); 10 | 11 | pre.literal-block, pre.doctest-block { 12 | margin-left: 1em ; 13 | margin-right: 1em ; 14 | background-color: #f4f4f4 } 15 | 16 | tt { background-color: transparent } 17 | -------------------------------------------------------------------------------- /doc/multi-threaded.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Multi-Threaded Quixote Applications 8 | 9 | 10 | 11 |
12 |

Multi-Threaded Quixote Applications

13 |

Starting with Quixote 0.6, it's possible to write multi-threaded Quixote 14 | applications. In previous versions, Quixote stored the current 15 | HTTPRequest object in a global variable, meaning that processing 16 | multiple requests in the same process simultaneously was impossible.

17 |

However, the Publisher class as shipped still can't handle multiple 18 | simultaneous requests; you'll need to subclass Publisher to make it 19 | re-entrant. Here's a starting point:

20 |
21 | import thread
22 | from quixote.publish import Publisher
23 | 
24 | [...]
25 | 
26 | class ThreadedPublisher (Publisher):
27 |     def __init__ (self, root_namespace, config=None):
28 |         Publisher.__init__(self, root_namespace, config)
29 |         self._request_dict = {}
30 | 
31 |     def _set_request(self, request):
32 |         self._request_dict[thread.get_ident()] = request
33 | 
34 |     def _clear_request(self):
35 |         try:
36 |             del self._request_dict[thread.get_ident()]
37 |         except KeyError:
38 |             pass
39 | 
40 |     def get_request(self):
41 |         return self._request_dict.get(thread.get_ident())
42 | 
43 |

Using ThreadedPublisher, you now have one current request per thread, 44 | rather than one for the entire process.

45 |

$Id: multi-threaded.txt 20217 2003-01-16 20:51:53Z akuchlin $

46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /doc/multi-threaded.txt: -------------------------------------------------------------------------------- 1 | Multi-Threaded Quixote Applications 2 | =================================== 3 | 4 | Starting with Quixote 0.6, it's possible to write multi-threaded Quixote 5 | applications. In previous versions, Quixote stored the current 6 | HTTPRequest object in a global variable, meaning that processing 7 | multiple requests in the same process simultaneously was impossible. 8 | 9 | However, the Publisher class as shipped still can't handle multiple 10 | simultaneous requests; you'll need to subclass Publisher to make it 11 | re-entrant. Here's a starting point:: 12 | 13 | import thread 14 | from quixote.publish import Publisher 15 | 16 | [...] 17 | 18 | class ThreadedPublisher (Publisher): 19 | def __init__ (self, root_namespace, config=None): 20 | Publisher.__init__(self, root_namespace, config) 21 | self._request_dict = {} 22 | 23 | def _set_request(self, request): 24 | self._request_dict[thread.get_ident()] = request 25 | 26 | def _clear_request(self): 27 | try: 28 | del self._request_dict[thread.get_ident()] 29 | except KeyError: 30 | pass 31 | 32 | def get_request(self): 33 | return self._request_dict.get(thread.get_ident()) 34 | 35 | Using ThreadedPublisher, you now have one current request per thread, 36 | rather than one for the entire process. 37 | 38 | 39 | $Id: multi-threaded.txt 20217 2003-01-16 20:51:53Z akuchlin $ 40 | -------------------------------------------------------------------------------- /doc/static-files.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Examples of serving static files 8 | 9 | 10 | 11 |
12 |

Examples of serving static files

13 |

The quixote.util module includes classes for making files and 14 | directories available as Quixote resources. Here are some examples.

15 |
16 |

Publishing a Single File

17 |

The StaticFile class makes an individual filesystem file (possibly 18 | a symbolic link) available. You can also specify the MIME type and 19 | encoding of the file; if you don't specify this, the MIME type will be 20 | guessed using the standard Python mimetypes.guess_type() function. 21 | The default action is to not follow symbolic links, but this behaviour 22 | can be changed using the follow_symlinks parameter.

23 |

The following example publishes a file with the URL .../stylesheet_css:

24 |
25 | # 'stylesheet_css' must be in the _q_exports list
26 | _q_exports = [ ..., 'stylesheet_css', ...]
27 | 
28 | stylesheet_css = StaticFile(
29 |         "/htdocs/legacy_app/stylesheet.css",
30 |         follow_symlinks=1, mime_type="text/css")
31 | 
32 |

If you want the URL of the file to have a .css extension, you use 33 | the external to internal name mapping feature of _q_exports. For 34 | example:

35 |
36 | _q_exports = [ ..., ('stylesheet.css', 'stylesheet_css'), ...]
37 | 
38 |
39 |
40 |

Publishing a Directory

41 |

Publishing a directory is similar. The StaticDirectory class 42 | makes a complete filesystem directory available. Again, the default 43 | behaviour is to not follow symlinks. You can also request that the 44 | StaticDirectory object cache information about the files in 45 | memory so that it doesn't try to guess the MIME type on every hit.

46 |

This example publishes the notes/ directory:

47 |
48 | _q_exports = [ ..., 'notes', ...]
49 | 
50 | notes = StaticDirectory("/htdocs/legacy_app/notes")
51 | 
52 |
53 |
54 | 55 | 56 | -------------------------------------------------------------------------------- /doc/static-files.txt: -------------------------------------------------------------------------------- 1 | Examples of serving static files 2 | ================================ 3 | 4 | The ``quixote.util`` module includes classes for making files and 5 | directories available as Quixote resources. Here are some examples. 6 | 7 | 8 | Publishing a Single File 9 | ------------------------ 10 | 11 | The ``StaticFile`` class makes an individual filesystem file (possibly 12 | a symbolic link) available. You can also specify the MIME type and 13 | encoding of the file; if you don't specify this, the MIME type will be 14 | guessed using the standard Python ``mimetypes.guess_type()`` function. 15 | The default action is to not follow symbolic links, but this behaviour 16 | can be changed using the ``follow_symlinks`` parameter. 17 | 18 | The following example publishes a file with the URL ``.../stylesheet_css``:: 19 | 20 | # 'stylesheet_css' must be in the _q_exports list 21 | _q_exports = [ ..., 'stylesheet_css', ...] 22 | 23 | stylesheet_css = StaticFile( 24 | "/htdocs/legacy_app/stylesheet.css", 25 | follow_symlinks=1, mime_type="text/css") 26 | 27 | 28 | If you want the URL of the file to have a ``.css`` extension, you use 29 | the external to internal name mapping feature of ``_q_exports``. For 30 | example:: 31 | 32 | _q_exports = [ ..., ('stylesheet.css', 'stylesheet_css'), ...] 33 | 34 | 35 | 36 | Publishing a Directory 37 | ---------------------- 38 | 39 | Publishing a directory is similar. The ``StaticDirectory`` class 40 | makes a complete filesystem directory available. Again, the default 41 | behaviour is to not follow symlinks. You can also request that the 42 | ``StaticDirectory`` object cache information about the files in 43 | memory so that it doesn't try to guess the MIME type on every hit. 44 | 45 | This example publishes the ``notes/`` directory:: 46 | 47 | _q_exports = [ ..., 'notes', ...] 48 | 49 | notes = StaticDirectory("/htdocs/legacy_app/notes") 50 | 51 | 52 | -------------------------------------------------------------------------------- /doc/ua_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Test Quixote's ability to parse the "User-Agent" header, ie. 4 | # the 'guess_browser_version()' method of HTTPRequest. 5 | # 6 | # Reads User-Agent strings on stdin, and writes Quixote's interpretation 7 | # of each on stdout. This is *not* an automated test! 8 | 9 | import sys, os 10 | from copy import copy 11 | from quixote.http_request import HTTPRequest 12 | 13 | env = copy(os.environ) 14 | file = sys.stdin 15 | while 1: 16 | line = file.readline() 17 | if not line: 18 | break 19 | if line[-1] == "\n": 20 | line = line[:-1] 21 | 22 | env["HTTP_USER_AGENT"] = line 23 | req = HTTPRequest(None, env) 24 | (name, version) = req.guess_browser_version() 25 | if name is None: 26 | print "%s -> ???" % line 27 | else: 28 | print "%s -> (%s, %s)" % (line, name, version) 29 | -------------------------------------------------------------------------------- /doc/upgrading.txt: -------------------------------------------------------------------------------- 1 | Upgrading code from older versions of Quixote 2 | ============================================= 3 | 4 | This document lists backward-incompatible changes in Quixote, and 5 | explains how to update application code to work with the newer 6 | version. 7 | 8 | Changes from 0.6.1 to 1.0 9 | ------------------------- 10 | 11 | Sessions 12 | ******** 13 | 14 | A leading underscore was removed from the ``Session`` attributes 15 | ``__remote_address``, ``__creation_time``, and ``__access_time``. If 16 | you have pickled ``Session`` objects you will need to upgrade them 17 | somehow. Our preferred method is to write a script that unpickles each 18 | object, renames the attributes and then re-pickles it. 19 | 20 | 21 | 22 | Changes from 0.6 to 0.6.1 23 | ------------------------- 24 | 25 | ``_q_exception_handler`` now called if exception while traversing 26 | ***************************************************************** 27 | 28 | ``_q_exception_handler`` hooks will now be called if an exception is 29 | raised during the traversal process. Quixote 0.6 had a bug that caused 30 | ``_q_exception_handler`` hooks to only be called if an exception was 31 | raised after the traversal completed. 32 | 33 | 34 | 35 | Changes from 0.5 to 0.6 36 | ----------------------- 37 | 38 | ``_q_getname`` renamed to ``_q_lookup`` 39 | *************************************** 40 | 41 | The ``_q_getname`` special function was renamed to ``_q_lookup``, 42 | because that name gives a clearer impression of the function's 43 | purpose. In 0.6, ``_q_getname`` still works but will trigger a 44 | warning. 45 | 46 | 47 | Form Framework Changes 48 | ********************** 49 | 50 | The ``quixote.form.form`` module was changed from a .ptl file to a .py 51 | file. You should delete or move the existing ``quixote/`` directory 52 | in ``site-packages`` before running ``setup.py``, or at least delete 53 | the old ``form.ptl`` and ``form.ptlc`` files. 54 | 55 | The widget and form classes in the ``quixote.form`` package now return 56 | ``htmltext`` instances. Applications that use forms and widgets will 57 | likely have to be changed to use the ``[html]`` template type to avoid 58 | over-escaping of HTML special characters. 59 | 60 | Also, the constructor arguments to ``SelectWidget`` and its subclasses have 61 | changed. This only affects applications that use the form framework 62 | located in the ``quixote.form`` package. 63 | 64 | In Quixote 0.5, the ``SelectWidget`` constructor had this signature:: 65 | 66 | def __init__ (self, name, value=None, 67 | allowed_values=None, 68 | descriptions=None, 69 | size=None, 70 | sort=0): 71 | 72 | ``allowed_values`` was the list of objects that the user could choose, 73 | and ``descriptions`` was a list of strings that would actually be 74 | shown to the user in the generated HTML. 75 | 76 | In Quixote 0.6, the signature has changed slightly:: 77 | 78 | def __init__ (self, name, value=None, 79 | allowed_values=None, 80 | descriptions=None, 81 | options=None, 82 | size=None, 83 | sort=0): 84 | 85 | The ``quote`` argument is gone, and the ``options`` argument has been 86 | added. If an ``options`` argument is provided, ``allowed_values`` 87 | and ``descriptions`` must not be supplied. 88 | 89 | The ``options`` argument, if present, must be a list of tuples with 90 | 1,2, or 3 elements, of the form ``(value:any, description:any, 91 | key:string)``. 92 | 93 | * ``value`` is the object that will be returned if the user chooses 94 | this item, and must always be supplied. 95 | 96 | * ``description`` is a string or htmltext instance which will be 97 | shown to the user in the generated HTML. It will be passed 98 | through the htmlescape() functions, so for an ordinary string 99 | special characters such as '&' will be converted to '&'. 100 | htmltext instances will be left as they are. 101 | 102 | * If supplied, ``key`` will be used in the value attribute 103 | of the option element (``