diff --git a/MANIFEST.in b/MANIFEST.in index 21b3e49..17947ac 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -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 diff --git a/doc/INSTALL.html b/doc/INSTALL.html deleted file mode 100644 index 120b0f7..0000000 --- a/doc/INSTALL.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - -
-
-

Installing Quixote

-

Quixote requires Python 2.3 or later.

-

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:

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

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

-

Now install the new version by running (in the distribution directory),

-
-python setup.py install
-

and you're done.

-
-
-

Quick start

-

In a terminal window, run server/simple_server.py. -In a browser, open http://localhost:8080

-
-
-

Upgrading a Quixote 1 application to Quixote 2.

-

See upgrading.txt for details.

-
-
- - diff --git a/doc/PTL.html b/doc/PTL.html deleted file mode 100644 index 4475a9a..0000000 --- a/doc/PTL.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - -PTL: Python Template Language - - - -
-

PTL: Python Template Language

-
-

Introduction

-

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.

-

Specifically, a PTL template is designated by inserting a [plain] -or [html] modifier after the function name. The value of -expressions inside templates are kept, not discarded. If the type is -[html] then non-literal strings are passed through a function that -escapes HTML special characters.

-
-
-

Plain text templates

-

Here's a sample plain text template:

-
-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
-    "."
-
-

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.

-

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:

-
-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
-
-

Calling this template with foo(1, 2) results in the following -string:

-
-This is a chunk of static text.You can plug in variables like x (1)
-in a variety of ways.
-
-

Normally when Python evaluates expressions inside functions, it just -discards their values, but in a [plain] PTL template the value is -converted to a string using str() and appended to the template's -return value. There's a single exception to this rule: None 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 None -would require assigning their value to a variable. You'd have to write -dummy = list.sort() in PTL code, which would be strange and -confusing.)

-

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 ...text.You can ... from the example. You'd -have to add an extra space to one of the string literals to correct -this.

-

The assignment to the greeting local variable is a statement, not an -expression, so it doesn't return a value and produces no output. The -output from the print 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. print should never be used -to generate output returned to the browser, only for adding debugging -traces to a template.

-

Inside templates, you can use all of Python's control-flow statements:

-
-def numbers [plain] (n):
-    for i in range(n):
-        i
-        " " # PTL does not add any whitespace
-
-

Calling numbers(5) will return the string "1 2 3 4 5 ". You can -also have conditional logic or exception blocks:

-
-def international_hello [plain] (language):
-    if language == "english":
-        "hello"
-    elif language == "french":
-        "bonjour"
-    else:
-        raise ValueError, "I don't speak %s" % language
-
-
-
-

HTML templates

-

Since PTL is usually used to generate HTML documents, an [html] -template type has been provided to make generating HTML easier.

-

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).

-

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).

-

Here's how this feature works. PTL defines a class called -htmltext that represents a string that's already been HTML-escaped -and can be safely sent to the client. The function htmlescape(string) -is used to escape data, and it always returns an htmltext -instance. It does nothing if the argument is already htmltext.

-

If a template function is declared [html] instead of [text] -then two things happen. First, all literal strings in the function -become instances of htmltext instead of Python's str. Second, -the values of expressions are passed through htmlescape() instead -of str().

-

htmltext type is like the str type except that operations -combining strings and htmltext instances will result in the string -being passed through htmlescape(). For example:

-
->>> 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'>
-
-

Note that calling str() strips the htmltext type and should be -avoided since it usually results in characters being escaped more than -once. While htmltext behaves much like a regular string, it is -sometimes necessary to insert a str() inside a template in order -to obtain a genuine string. For example, the re module requires -genuine strings. We have found that explicit calls to str() can -often be avoided by splitting some code out of the template into a -helper function written in regular Python.

-

It is also recommended that the htmltext constructor be used as -sparingly as possible. The reason is that when using the htmltext -feature of PTL, explicit calls to htmltext become the most likely -source of cross-site scripting holes. Calling htmltext 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."

-

Note that literal strings in template functions declared with -[html] are htmltext instances, and therefore won't be escaped. -You'll only need to use htmltext when HTML markup comes from -outside the template. For example, if you want to include a file -containing HTML:

-
-def output_file [html] ():
-    '<html><body>' # does not get escaped
-    htmltext(open("myfile.html").read())
-    '</body></html>'
-
-

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 HEAD element:

-
-def meta_tags [html] (title, description):
-    '<title>%s</title>' % title
-    '<meta name="description" content="%s">\n' % description
-
-

There are no calls to htmlescape() at all, but string literals -such as <title>%s</title> have all be turned into htmltext -instances, so the string variables will be automatically escaped:

-
->>> 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'>
->>>
-
-

Note how the title and description have had HTML-escaping applied to them. -(The output has been manually pretty-printed to be more readable.)

-

Once you start using htmltext in one of your templates, mixing -plain and HTML templates is tricky because of htmltext'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 str() to convert -htmltext instances to regular Python strings; just be sure the -resulting string isn't HTML-escaped again.

-

Two implementations of htmltext are provided, one written in pure -Python and a second one implemented as a C extension. Both versions -have seen production use.

-
-
-

PTL modules

-

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 .pyc. 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 enable_ptl() function:

-
-from quixote import enable_ptl
-enable_ptl()
-
-

(Note: if you're using ZODB, always import ZODB before 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.)

-

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 foo.ptl, you could then write Python code that did -this:

-
-from foo import numbers
-def f():
-    return numbers(10)
-
-

You may want to keep this little function in your PYTHONSTARTUP -file:

-
-def ptl():
-    try:
-        import ZODB
-    except ImportError:
-        pass
-    from quixote import enable_ptl
-    enable_ptl()
-
-

This is useful if you want to interactively play with a PTL module.

-
-
- - diff --git a/doc/demo.html b/doc/demo.html deleted file mode 100644 index eca8f29..0000000 --- a/doc/demo.html +++ /dev/null @@ -1,206 +0,0 @@ - - - - - - -Running the Quixote Demos - - - -
-

Running the Quixote Demos

-

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.

-

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 http://localhost:8080.

-

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:

-
-simple_server.py --factory quixote.demo.mini_demo.create_publisher
-

or

-
-simple_server.py --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
-
-

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 -messages from the web server.

-

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.

-

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', ''].

-

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/

-

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'.

-

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.

-

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.

-

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.

-
-
-

Understanding the root demo

-
-
Start the root demo by running the command:
-
simple_server.py --factory quixote.demo.create_publisher
-
-

In a browser, open http://localhost:8080 as before. -Click around at will.

-

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.

-

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.

-

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.

-

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.

-

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.

-
-
-

The _q_lookup() method

-

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.

-

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 http://localhost:8080/extras/12/ to see how -this behaves.

-

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 -http://localhost:8080/extras/12 to yield the same page as -http://localhost:8080/extras/12/. If given the path ['extras', '12'], -the default _q_traverse() ends up calling 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__]

-

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.

-
-
-

The _q_resolve() method

-

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 have that attribute, the _q_resolve() method is called to -"resolve" the trouble. Typically, the _q_resolve() imports or -constructs what should 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.

-
-
-

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.

-
-
- - diff --git a/doc/form2conversion.html b/doc/form2conversion.html deleted file mode 100644 index 4f4f3a5..0000000 --- a/doc/form2conversion.html +++ /dev/null @@ -1,377 +0,0 @@ - - - - - - -Converting form1 forms to use the form2 library - - - -
-

Converting form1 forms to use the form2 library

-
-

Note:

-

The packages names have changed in Quixote 2.

-

Quixote form1 forms are now in the package quixote.form1. -(In Quixote 1, they were in quixote.form.)

-

Quixote form2 forms are now in the package quixote.form. -(In Quixote 1, they were in quixote.form2.)

-
-
-

Introduction

-

These are some notes and examples for converting Quixote form1 forms, -that is forms derived from quixote.form1.Form, to the newer form2 -forms.

-

Form2 forms are more flexible than their form1 counterparts in that they -do not require you to use the Form 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 process, render, -and action is desirable.

-

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.

-

The other end of the spectrum and normally the easiest way to port -form1 forms to form2 is to use the compatibility module provided -in the form2 package. The compatibility module's Form class provides -much of the same highly structured machinery (via a handle master -method) that the form1 framework uses.

-
-
-

Converting form1 forms using using the compatibility module

-

Here's the short list of things to do to convert form1 forms to -form2 using compatibility.

-
-
    -
  1. Import the Form base class from quixote.form.compatibility -rather than from quixote.form1.

    -
  2. -
  3. 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.

    -
    -

    Form1 API:

    -
    -self.error['widget_name'] = 'the error message'
    -
    -

    Form2 API:

    -
    -self.set_error('widget_name', 'the error message') 
    -
    -
    -

    If you want to find out if the form already has errors, change -the form1 style of direct references to the self.errors -dictionary to a call to the has_errors method.

    -
    -

    Form1 API:

    -
    -if not self.error:
    -   do some more error checking...
    -
    -

    Form2 API:

    -
    -if not self.has_errors():
    -   do some more error checking...
    -
    -
    -
  4. -
  5. Form2 select widgets no longer take allowed_values or -descriptions arguments. If you are adding type of form2 select -widget, you must provide the options 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:

    -
    -    options: [objects:any]
    -or
    -    options: [(object:any, description:any)]
    -or
    -    options: [(object:any, description:any, key:any)]
    -
    -

    An easy way to construct options if you already have -allowed_values and descriptions is to use the built-in function -zip to define options:

    -
    -options=zip(allowed_values, descriptions)
    -
    -
  6. -
-
-Note, however, that often it is simpler to to construct the -options list directly.
-
    -
  1. 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 quixote.form.css.
  2. -
-
-

Here's the longer list of things you may need to tweak in order for -form2 compatibility forms to work with your form1 code.

-
-
    -
  • widget_type widget class attribute is gone. This means when -adding widgets other than widgets defined in quixote.form.widget, -you must import the widget class into your module and pass the -widget class as the first argument to the add_widget method -rather than using the widget_type string.

    -
  • -
  • The action_url argument to the form's render method is now -a keyword argument.

    -
  • -
  • If you use OptionSelectWidget, there is no longer a -get_current_option method. You can get the current value -in the normal way.

    -
  • -
  • ListWidget has been renamed to WidgetList.

    -
  • -
  • There is no longer a CollapsibleListWidget class. If you need -this functionality, consider writing a 'deletable composite widget' -to wrap your WidgetList widgets in it:

    -
    -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)
    -
    -
  • -
-
-

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:

-
-
    -
  • In your process method, you don't really need to get a form_data -dictionary by calling Form.process to ensure your widgets are -parsed. Instead, the parsed value of any widget is easy to obtain -using the widget's get_value method or the form's -__getitem__ method. So, instead of:

    -
    -form_data = Form.process(self, request)
    -val = form_data['my_widget']
    -
    -

    You can use:

    -
    -val = self['my_widget']
    -
    -

    If the widget may or may not be in the form, you can use get:

    -
    -val = self.get('my_widget')
    -
    -
  • -
  • It's normally not necessary to provide the action_url argument -to the form's render method.

    -
  • -
  • 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 add (or add_widget in -the compatibility module) can be retrieved using the form's -get_widget method.

    -
  • -
-
-
-
-

Converting form1 forms to form2 by functional rewrite

-

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.

-

First the form1 form:

-
-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) + '/')
-
-

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:

-
-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)
-
-

As you can see in the example, the function still has all of the same -parts of it's form1 equivalent.

-
-
    -
  1. It determines if it's to create a new object or edit an existing one
  2. -
  3. It adds submit buttons and widgets
  4. -
  5. It has a function that knows how to render the form
  6. -
  7. It has a function that knows how to do error processing on the form
  8. -
  9. It has a function that knows how to register permanent changes to -objects when the form is submitted successfully.
  10. -
-
-

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.

-

Form2 functional forms do not have the handle master-method that -is called after the form is initialized. Instead, we deal with this -functionality manually. Here are some things that the handle -portion of your form might need to implement illustrated in the -order that often makes sense.

-
-
    -
  1. Get the value of any submit buttons using form.get_submit
  2. -
  3. If the form has not been submitted yet, return render().
  4. -
  5. See if the cancel button was pressed, if so return a redirect.
  6. -
  7. Call your process 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.
  8. -
  9. 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 get_submit is True in this case and if it is, -you want to clear any errors and re-render the form.
  10. -
  11. If the form has not been submitted or if the form has errors, -you simply want to render the form.
  12. -
  13. Check for your named submit buttons which you expect for -successful form posting e.g. add or update. If one of -these is pressed, call you action inner function.
  14. -
  15. Finally, return a redirect to the expected page following a -form submission.
  16. -
-
-

These steps are illustrated by the following snippet of code and to a -large degree in the above functional form2 code example. Often this -handle 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 process function or the call to it:

-
-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)
-
-
-
- - diff --git a/doc/multi-threaded.html b/doc/multi-threaded.html deleted file mode 100644 index 763c2f4..0000000 --- a/doc/multi-threaded.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - -Multi-Threaded Quixote Applications - - - -
-

Multi-Threaded Quixote Applications

-

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.

-

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:

-
-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())
-
-

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

-
- - diff --git a/doc/programming.html b/doc/programming.html deleted file mode 100644 index dfef0e2..0000000 --- a/doc/programming.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - -Quixote Programming Overview - - - -
-

Quixote Programming Overview

-

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:

-
    -
  1. 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.

    -
  2. -
  3. 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.

    -

    The most important configuration parameters are:

    -
    -
    -
    ERROR_EMAIL
    -

    e-mail address to which errors will be mailed

    -
    -
    ERROR_LOG
    -

    file to which errors will be logged

    -
    -
    -
    -

    For development/debugging, you should also set DISPLAY_EXCEPTIONS -true; the default value is false, to favor security over convenience.

    -
  4. -
  5. 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 root_directory. Normally, the root_directory will be an -instance of the Directory class.

    -
  6. -
-
-

Driver script

-

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:

- -

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.

-

The most important application-specific customization done by the driver -script is to set the root directory of your application.

-

The quixote.servers package includes driver modules for cgi, fastcgi, -scgi, medusa, twisted, and the simple_server. Each of these modules -includes a run() 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 run() 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.)

-

That's almost the simplest possible case -- there's no -application-specific configuration info apart from the root directory.

-

Getting the driver script to actually run is between you and your web -server. See the web-server.txt document for help.

-
-
-

Configuration file

-

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.

-
-
-

Logging

-

The publisher also accepts an optional logger keyword argument, -that should, if provided, support the same methods as the -default value, an instance of DefaultLogger. Even if you -use the default logger, you can still customize the behavior -by setting configuration values for access_log, error_log, and/or -error_email. These configuration variables are described -more fully in config.py.

-

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:

-
-127.0.0.1 - 2001-10-15 09:48:43
-  2504 "GET /catalog/ HTTP/1.1"
-  200 'Opera/6.0 (Linux; U)' 0.10sec
-
-

This line consists of:

- -

If no access log is configured (ie., ACCESS_LOG is None), then -Quixote will not do any access logging.

-

The error log is used for three purposes:

- -

If no error log is configured (with ERROR_LOG), 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.

-

Having stdout redirected to the error log is useful for debugging. You -can just sprinkle print statements into your application and the -output will wind up in the error log.

-
-
-

Application code

-

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.

-

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.

-
-
- - diff --git a/doc/session-mgmt.html b/doc/session-mgmt.html deleted file mode 100644 index aafac11..0000000 --- a/doc/session-mgmt.html +++ /dev/null @@ -1,307 +0,0 @@ - - - - - - -Quixote Session Management - - - -
-

Quixote Session Management

-

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.

-

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.)

-

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.

-

In a nutshell, session management with Quixote works like this:

- -

There are two caveats to keep in mind before proceeding, one major and -one minor:

- -
-

Session management demo

-

There's a simple demo of Quixote's session management in demo/altdemo.py. -If the durus (http://www.mems-exchange.org/software/durus/) 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.

-

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 user 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.

-

Tracking the number of requests is a bit more interesting: from the -DemoSession class in altdemo.py:

-
-def __init__ (self, id):
-    Session.__init__(self, id)
-    self.num_requests = 0
-
-def start_request (self):
-    Session.start_request(self)
-    self.num_requests += 1
-
-

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 num_requests in the demo) or methods of the -current Session instance.

-

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:

-
-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'))
-
-

Note that we must quote the user's name, because they are free to enter -anything they please, including special HTML characters like & or -<.

-

Of course, session.user will never be set if we don't set it -ourselves. The code that processes the login form is just this (from -login() in demo/altdemo.py)

-
-if get_field("name"):
-    session = get_session()
-    session.set_user(get_field("name")) # This is the important part.
-
-

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.

-
- -
-

Writing the session class

-

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:

- -

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.

-

For development/testing work, it's fine to say that your session objects -should always be saved:

-
-def has_info (self):
-    return 1
-
-

The opposite extreme is to forget to override has_info() altogether, -in which case session management most likely won't work: unless you -tickle the Session object such that the base has_info() 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.

-

In a real application, you need to think carefully about what data to -store in your sessions, and how has_info() 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 robots.txt will -come along and crawl your entire site, wreaking havoc on your Quixote -application (or the database underlying it).

-
-
-

Session persistence

-

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.

-

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.

-

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.

-

The second hook (two hooks, really) apply if you use a transactional -persistence mechanism to provide your SessionManager's mapping. The -altdemo.py 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: forget_changes() and -commit_changes(). forget_changes() is called by -SessionPublisher whenever a request crashes, ie. whenever your -application raises an exception other than PublishError. -commit_changes() 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.

-

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:

-
-import shelve
-sessions = shelve.open("/tmp/quixote-sessions")
-session_manager = SessionManager(session_mapping=sessions)
-
-

If you use one of these relatively simple persistent mapping types, -you'll also need to override is_dirty() in your Session class. -That's in addition to overriding has_info(), which determines if a -session object is ever saved; is_dirty() 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, is_dirty() 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 -has_info() will work -- that just means that once the session has -been written once, it will be re-written on every request.

-
-
- - diff --git a/doc/static-files.html b/doc/static-files.html deleted file mode 100644 index 7bab3f5..0000000 --- a/doc/static-files.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - -Examples of serving static files - - - -
-

Examples of serving static files

-

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

-
-

Publishing a Single File

-

The StaticFile 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 mimetypes.guess_type() function. -The default action is to not follow symbolic links, but this behaviour -can be changed using the follow_symlinks parameter.

-

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

-
-# '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")
-
-

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

-
-_q_exports = [ ..., ('stylesheet.css', 'stylesheet_css'), ...]
-
-
-
-

Publishing a Directory

-

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

-

This example publishes the notes/ directory:

-
-_q_exports = [ ..., 'notes', ...]
-
-notes = StaticDirectory("/htdocs/legacy_app/notes")
-
-
-
- - diff --git a/doc/upgrading.html b/doc/upgrading.html deleted file mode 100644 index e425842..0000000 --- a/doc/upgrading.html +++ /dev/null @@ -1,293 +0,0 @@ - - - - - - -Upgrading code from older versions of Quixote - - - -
-

Upgrading code from older versions of Quixote

-

This document lists backward-incompatible changes in Quixote, and -explains how to update application code to work with the newer -version.

-
-

Changes from 1.0 to 2.0

-

Change any imports you have from quixote.form to be from quixote.form1.

-

Change any imports you have from quixote.form2 to be from quixote.form.

-

Replace calls to HTTPRequest.get_form_var() with calls to get_field().

-

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.

-

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.

-

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.

-

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.

-

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.

-

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.

-

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().

-

If your code depended on _q_access, include the AccessControlled with -the bases of your Directory classes as needed.

-

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.

-

Quixote 1's secure_errors configuration variable is not present in Quixote 2.

-

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 -'**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'.

-
-

System Message: WARNING/2 (upgrading.txt, line 65); backlink

-Inline strong start-string without end-string.
-

The SessionPublisher class is gone. Use the Publisher class instead. -Also, the 'session_mgr' keyword has been renamed to 'session_manager'.

-
-
-

Changes from 0.6.1 to 1.0

-
-

Sessions

-

A leading underscore was removed from the Session attributes -__remote_address, __creation_time, and __access_time. If -you have pickled Session 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.

-
-
-
-

Changes from 0.6 to 0.6.1

-
-

_q_exception_handler now called if exception while traversing

-

_q_exception_handler hooks will now be called if an exception is -raised during the traversal process. Quixote 0.6 had a bug that caused -_q_exception_handler hooks to only be called if an exception was -raised after the traversal completed.

-
-
-
-

Changes from 0.5 to 0.6

-
-

_q_getname renamed to _q_lookup

-

The _q_getname special function was renamed to _q_lookup, -because that name gives a clearer impression of the function's -purpose. In 0.6, _q_getname still works but will trigger a -warning.

-
-
-

Form Framework Changes

-

The quixote.form.form module was changed from a .ptl file to a .py -file. You should delete or move the existing quixote/ directory -in site-packages before running setup.py, or at least delete -the old form.ptl and form.ptlc files.

-

The widget and form classes in the quixote.form package now return -htmltext instances. Applications that use forms and widgets will -likely have to be changed to use the [html] template type to avoid -over-escaping of HTML special characters.

-

Also, the constructor arguments to SelectWidget and its subclasses have -changed. This only affects applications that use the form framework -located in the quixote.form package.

-

In Quixote 0.5, the SelectWidget constructor had this signature:

-
-def __init__ (self, name, value=None,
-              allowed_values=None,
-              descriptions=None,
-              size=None,
-              sort=0):
-
-

allowed_values was the list of objects that the user could choose, -and descriptions was a list of strings that would actually be -shown to the user in the generated HTML.

-

In Quixote 0.6, the signature has changed slightly:

-
-def __init__ (self, name, value=None,
-              allowed_values=None,
-              descriptions=None,
-              options=None,
-              size=None,
-              sort=0):
-
-

The quote argument is gone, and the options argument has been -added. If an options argument is provided, allowed_values -and descriptions must not be supplied.

-

The options argument, if present, must be a list of tuples with -1,2, or 3 elements, of the form (value:any, description:any, -key:string).

-
-
    -
  • value is the object that will be returned if the user chooses -this item, and must always be supplied.
  • -
  • description 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.
  • -
  • If supplied, key will be used in the value attribute -of the option element (<option value="...">). -If not supplied, keys will be generated; value is checked for a -_p_oid attribute and if present, that string is used; -otherwise the description is used.
  • -
-
-

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.

-
-
-

File Upload Changes

-

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.

-

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.

-

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:

-
-
orig_filename
-
the filename supplied by the browser.
-
base_filename
-
a stripped-down version of orig_filename with unsafe characters removed. -This could be used when writing uploaded data to a permanent location.
-
tmp_filename
-
the path of the temporary file containing the uploaded data for this field.
-
-

Consult upload.txt for more information about handling file uploads.

-
-
-

Refactored Publisher Class

-

Various methods in the Publisher class were rearranged. If your -application subclasses Publisher, you may need to change your code -accordingly.

-
-
    -
  • parse_request() no longer creates the HTTPRequest object; -instead a new method, create_request(), handles this, -and can be overridden as required.

    -

    As a result, the method signature has changed from -parse_request(stdin, env) to parse_request(request).

    -
  • -
  • The Publisher.publish() method now catches exceptions raised -by parse_request().

    -
  • -
-
-
-
-
-

Changes from 0.4 to 0.5

-
-

Session Management Changes

-

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.

-
-

Attribute renamings and pickled objects

-

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:

-
-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
-
-

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:

-
-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
-
-

It's not pretty, but it worked for us.

-
- -
-
-
- - diff --git a/doc/web-server.html b/doc/web-server.html deleted file mode 100644 index 8833cb8..0000000 --- a/doc/web-server.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - -Web Server Configuration for Quixote - - - -
-

Web Server Configuration for Quixote

-

For a simple Quixote installation, there are two things you have to get -right:

- -

This document is concerned with the second of these.

-
-

Which web servers?

-

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 PATH_INFO), then you can -run Quixote applications in the ideal manner, ie. with superfluous -implementation details hidden from the user.

-
-
-

Which operating systems?

-

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.

-

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.

-
-
-

Basic CGI configuration

-

Throughout this document, I'm going to assume that:

- -

With Apache, these configuration directives will do the trick:

-
-AddHandler cgi-script .cgi
-ScriptAlias /cgi-bin/ /www/cgi-bin/
-
-

Consult the Apache documentation for other ways of configuring CGI -script execution.

-

For other web servers, consult your server's documentation.

-
-
-

Installing driver scripts

-

Given the above configuration, installing a Quixote driver script is the -same as installing any other CGI script: copy it to /www/cgi-bin (or -whatever). To install the Quixote demo's cgi driver script:

-
-cp -p server/cgi_server.py /www/cgi-bin/demo.cgi
-
-

(The -p option ensures that cp preserves the file mode, so that -it remains executable.)

-
-
-

URL rewriting

-

With the above configuration, users need to use URLs like

-
-http://www.example.com/cgi-bin/demo.cgi
-
-

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.

-

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

-
-http://www.example.com/qdemo
-
-

to handle the Quixote demo.

-

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.)

-

To enable the rewrite engine, use the

-
-RewriteEngine on
-
-

directive. If you have virtual hosts, make sure to repeat this for each -<VirtualHost> section of your config file.

-

The rewrite rule to use in this case is

-
-RewriteRule ^/qdemo(/.*) /www/cgi-bin/demo.cgi$1 [last]
-
-

This is not a redirect; this is all handled with one HTTP -request/response cycle, and the user never sees /cgi-bin/demo.cgi in -a URL.

-

Note that requests for /qdemo/ and /qdemo are not 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: -/qdemo doesn't match the regex, so demo.cgi is never invoked.)

-

The solution for /qdemo is the same as if it corresponded to a -directory in your document tree: redirect it to /qdemo/. Apache -(and, presumably, other web servers) does this automatically for "real" -directories; however, /qdemo/ is just a directory-like chunk of -URL-space, so either you or Quixote have to take care of the redirect.

-

It's almost certainly faster for you to take care of it in the web -server's configuration. With Apache, simply insert this directive -before the above rewrite rule:

-
-RewriteRule ^/qdemo$ /qdemo/ [redirect=permanent]
-
-

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 /qdemo URL is handled by -Quixote. Change the rewrite rule to:

-
-RewriteRule ^/qdemo(/.*)?$ /www/cgi-bin/demo.cgi$1 [last]
-
-

Now a request for /qdemo will be handled by Quixote, and it will -generate a redirect to /qdemo/. If you're using a CGI driver -script, this will be painfully slow, but it will work.

-

For redirecting and rewriting URLs with other web servers, consult your -server's documentation.

-
-
-

Long-running processes

-

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.

-

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.

-
-
-

mod_scgi configuration

-

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.

-

The SCGI code is available from http://www.mems-exchange.org/software/scgi/ .

-

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 run() -function with arguments to run your application, on the port -you choose. Here is an example:

-
-#!/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)
-
-

The following Apache directive will direct requests to an SCGI server -running on port 3001:

-
-<Location />
-  SCGIServer 127.0.0.1 3001
-  SCGIHandler On
-</Location>
-
-

[Note: the mod_scgi module for Apache 2 requires a colon, instead of a -space, between the host and port on the SCGIServer line.]

-
-
-

SCGI through CGI

-

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 -DPORT=3001 (for example) to your compile command.

-

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.

-
-
-

FastCGI configuration

-

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 -http://www.fastcgi.com/ for more information on FastCGI.)

-

To use FastCGI with Apache, you'll need to download mod_fastcgi from -http://www.fastcgi.com/ and add it to your Apache installation.

-

Configuring a FastCGI driver script is best done after reading the fine -documentation for mod_fastcgi at -http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html

-

However, if you just want to try it with the Quixote demo to see if it -works, add this directive to your Apache configuration:

-
-AddHandler fastcgi-script .fcgi
-
-

and copy server/fastcgi_server.py to demo.fcgi. If you're using a URL -rewrite to map requests for (eg.) /qdemo to -/www/cgi-bin/demo.cgi, be sure to change the rewrite -- it should -now point to /www/cgi-bin/demo.fcgi.

-

After the first access to demo.fcgi (or /qdemo/ with the -modified rewrite rule), the demo should be noticeably faster. You -should also see a demo.fcgi process running if you do ps -le -(ps -aux on BSD-ish systems, or maybe ps aux). (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).

-
-
- - diff --git a/doc/web-services.html b/doc/web-services.html deleted file mode 100644 index 6789439..0000000 --- a/doc/web-services.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - -Implementing Web Services with Quixote - - - -
-

Implementing Web Services with Quixote

-

This document will show you how to implement Web services using -Quixote.

-
-

An XML-RPC Service

-

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 -http://www.pythonware.com/products/xmlrpc/, converts between Python's -standard data types and the XML-RPC data types.

- ---- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
XML-RPC TypePython Type or Class
<int>int
<double>float
<string>string
<array>list
<struct>dict
<boolean>xmlrpclib.Boolean
<base64>xmlrpclib.Binary
<dateTime>xmlrpclib.DateTime
-
-
-

Making XML-RPC Calls

-

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.

-
->>> import xmlrpclib
->>> s = xmlrpclib.ServerProxy(
-             'http://www.stuffeddog.com/speller/speller-rpc.cgi')
-
-

Now you can simply make a call to the spell-checking service offered -by this server:

-
->>> 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}]
->>> 
-
-

This call results in the following XML being sent:

-
-<?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>
-
-
-
-

Writing a Quixote Service

-

In the quixote.util module, Quixote provides a function, -xmlrpc(request, func), that processes the body of an XML-RPC -request. request is the HTTPRequest object that Quixote passes to -every function it invokes. func 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 xmlrpc() function will -catch the exception and return a Fault to the remote caller.

-

Here's an example of implementing a simple XML-RPC handler with a -single method, get_time(), that simply returns the current -time. The first task is to expose a URL for accessing the service.

-
-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):
-    ...
-
-

When the above code is placed in the __init__.py file for the Python -package corresponding to your Quixote application, it exposes the URL -http://<hostname>/rpc as the access point for the XML-RPC service.

-

Next, we need to fill in the contents of the rpc_process() -function:

-
-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
-
-

rpc_process() 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 rpc_process() will -therefore usually be an if statement that checks the name of the -method, and calls another function to do the actual work. In this case, -get_time() is very simple so the two lines of code it requires are -simply included in the body of rpc_process().

-

If the method name doesn't belong to a supported method, execution -will fall through to the else clause, which will raise a -RuntimeError exception. Quixote's xmlrpc() will catch this -exception and report it to the caller as an XML-RPC fault, with the -error code set to 1.

-

As you add additional XML-RPC services, the if statement in -rpc_process() will grow more branches. You might be tempted to pass -the method name to getattr() 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 if... elif... elif... else 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.

-

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:

-
-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
-
-

This approach works nicely when there are many methods and the -if...elif...else statement would be unworkably long.

-
-
- - diff --git a/doc/widgets.html b/doc/widgets.html deleted file mode 100644 index 1ca44af..0000000 --- a/doc/widgets.html +++ /dev/null @@ -1,502 +0,0 @@ - - - - - - -Quixote Widget Classes - - - -
-

Quixote Widget Classes

-

[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 HTML: The Definitive -Guide by Chuck Musciano & Bill Kennedy (O'Reilly). I usually keep it -within arm's reach.]

-

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:

-
-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 
-
-
-

Widget: the base class

-

Widget is the abstract base class for the widget hierarchy. It provides -the following facilities:

- -

The Widget constructor signature is:

-
-Widget(name : string, value : any = None)
-
-
-
name
-
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.
-
value
-
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.
-
-
-
-

Common widget methods

-

The Widget base class also provides a couple of useful -methods:

-
-
set_name(name:string)
-
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.
-
set_value(value:any)
-
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).
-
clear()
-
clear the widget's current value. Equivalent to -widget.set_value(None).
-
-

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:

-
-
render(request:HTTPRequest) : string
-
return a chunk of HTML that implements the form element -corresponding to this widget.
-
parse(request:HTTPRequest) : any
-
extract the form value for this widget from request.form, 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 set_value().
-
-
-
-

StringWidget

-

Used for short, single-line string input with no validation (ie. any -string will be accepted.) Generates an <input type="text"> form -element.

-
-

Constructor

-
-StringWidget(name : string,
-             value : string = None,
-             size : int = None,
-             maxlength : int = None)
-
-
-
size
-
used as the size attribute of the generated <input> tag; -controls the physical size of the input field.
-
maxlength
-
used as the maxlength attribute; controls the maximum amount -of input.
-
-
-
-

Examples

-
->>> 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">'
-
-
-
-
-

PasswordWidget

-

PasswordWidget is identical to StringWidget except for the type of the -HTML form element: password instead of text.

-
-
-

TextWidget

-

Used for multi-line text input. The value is a single string with -newlines right where the browser supplied them. (\r\n, if present, -is converted to \n.) Generates a <textarea> form element.

-
-

Constructor

-
-TextWidget(name : string,
-           value : string = None,
-           cols : int = None,
-           rows : int = None,
-           wrap : string = "physical")
-
-
-
cols, rows
-
number of columns/rows in the textarea
-
wrap
-
controls how the browser wraps text and includes newlines in the -submitted form value; consult an HTML book for details.
-
-
-
-
-

CheckboxWidget

-

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 parse() will always be 0 or 1 (but you shouldn't -rely on that!). Generates an <input type="checkbox"> form element.

-
-

Constructor

-
-CheckboxWidget(name : string,
-               value : boolean = false)
-
-
-
-

Examples

-
->>> 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>'
-
-
-
-
-

RadiobuttonsWidget

-

Used for a set of related radiobuttons, ie. several <input -type="radio"> tags with the same name and different values. The set -of values are supplied to the constructor as allowed_values, which -may be a list of any Python objects (not just strings). The current -value must be either None (the default) or one of the values in -allowed_values; if you supply a value not in allowed_values, -it will be ignored. parse() will return either None or one of -the values in allowed_values.

-
-

Constructor

-
-RadiobuttonsWidget(name : string,
-                   value : any = None,
-                   allowed_values : [any] = None,
-                   descriptions : [string] = map(str, allowed_values),
-                   quote : boolean = true,
-                   delim : string = "\n")
-
-
-
allowed_values
-

specifies how many <input type="radio"> tags to generate and the -values for each. Eg. allowed_values=["foo", "bar"] will result in -(roughly):

-
-<input type="radio" value="foo">
-<input type="radio" value="bar">
-
-
-
descriptions
-
the text that will actually be shown to the user in the web page -that includes this widget. Handy when the elements of -allowed_values are too terse, or don't have a meaningful -str(), or you want to add some additional cues for the user. If -not supplied, map(str, allowed_values) is used, with the -exception that None in allowed_values becomes "" (the -empty string) in descriptions. If supplied, descriptions -must be the same length as allowed_values.
-
quote
-
if true (the default), the elements of 'descriptions' will be -HTML-quoted (using quixote.html.html_quote()) when the widget is -rendered. This is essential if you might have characters like -& or < in your descriptions. However, you'll want to set -quote to false if you are deliberately including HTML markup -in your descriptions.
-
delim
-
the delimiter to separate the radiobuttons with when rendering -the whole widget. The default ensures that your HTML is readable -(by putting each <input> tag on a separate line), and that there -is horizontal whitespace between each radiobutton.
-
-
-
-

Examples

-
->>> 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>
-
-

(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 "1" to 1 and looking up allowed_values[1].)

-
->>> 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>
-
-
-
-
-

SingleSelectWidget

-

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; parse() will return either -None or one of the values you supply to the constructor as -allowed_values. Generates a <select>...</select> tag, with one -<option> tag for each element of allowed_values.

-
-

Constructor

-
-SingleSelectWidget(name : string,
-                   value : any = None,
-                   allowed_values : [any] = None,
-                   descriptions : [string] = map(str, allowed_values),
-                   quote : boolean = true,
-                   size : int = None)
-
-
-
allowed_values
-
determines the set of <option> tags that will go inside the -<select> tag; these can be any Python values (not just strings). -parse() will return either one of the allowed_values or None. -If you supply a value that is not in allowed_values, it -will be ignored.
-
descriptions
-
(same as RadiobuttonsWidget above)
-
quote
-
(same as RadiobuttonsWidget above)
-
size
-
corresponds to the size attribute of the <select> tag: ask -the browser to show a select list with size items visible. -Not always respected by the browser; consult an HTML book.
-
-
-
-

Examples

-
->>> 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>
-
-
-
-
-

MultipleSelectWidget

-

Used to select multiple values from a list. Everything is just like -SingleSelectWidget, except that value can be a list of objects -selected from allowed_values (in which case every object in value -will initially be selected). Generates a <select multiple>...</select> -tag, with one <option> tag for each element of allowed_values.

-
-

Constructor

-
-MultipleSelectWidget(name : string,
-                     value : any | [any] = None,
-                     allowed_values : [any] = None,
-                     descriptions : [string] = map(str, allowed_values),
-                     quote : boolean = true,
-                     size : int = None)
-
-
-
-
-

SubmitButtonWidget

-

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.

-

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.

-

Also, SubmitButtonWidget is the only widget with an optional name. -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.

-
-

Constructor

-
-SubmitButtonWidget(name : string = None,
-                   value : string = None)
-
-
-
value
-
the text that will be shown in the user's browser, and the -value that will be returned for this form element (widget) -if the user selects this submit button.
-
-
-
-

Examples

-
-
->>> SubmitButtonWidget(value="Submit Form").render(request)
-'<input type="submit" value="Submit Form">'
-
-
-
-
-
-

HiddenWidget

-

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.)

-
-

Constructor

-
-HiddenWidget(name : string,
-             value : string)
-
-
-
-

Examples

-
->>> HiddenWidget("form_id", "2452345135").render(request)
-'<input type="hidden" name="form_id" value="2452345135">'
-
-
-
-
-

IntWidget

-

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 int() can't convert to an integer, -IntWidget's parse() 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.

-

IntWidget.parse() always returns an integer or None.

-
-

Constructor

-
-IntWidget(name : string,
-          value : int = None,
-          size : int = None,
-          maxlength : int = None)
-
-

Constructor arguments are as for StringWidget, except that value -must be an integer (or None). Note that size and -maxlength have exactly the same meaning: they control the size of -the input widget and the maximum number of characters of input.

-

[Examples]

-
-
->>> IntWidget("num", value=37, size=5).render(request)
-'<input type="string" name="num" value="37" size="5">'
-
-
-
-
-
-

FloatWidget

-

FloatWidget is identical to IntWidget, except:

- -
-
-

OptionSelectWidget

-

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.)

-

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.)

-

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 submit, -since that masks the Javascript function called by OptionSelectWidget.

-
-
- - diff --git a/doc/wsgi.html b/doc/wsgi.html deleted file mode 100644 index 863ea2e..0000000 --- a/doc/wsgi.html +++ /dev/null @@ -1,315 +0,0 @@ - - - - - - -WSGI and Quixote - - - -
-

WSGI and Quixote

-

"WSGI" stands for the Python Web Server Gateway Interface, defined in -PEP 333.

-

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)
-
-
-
- -