How Quixote Works

来自开放百科 - 灰狐
跳转到: 导航, 搜索


The basic idea

Take the URL path and split it apart: 
   → ['catalog', 'item', 'details']

Starting at a configured root package (e.g. 'store.web'), follow the URL path down:   →    store.web._q_index()
http://.../catalog/   →    store.web.catalog()  
                       or    store.web.catalog._q_index

Special names

  • _q_index: If traversal ends up at an object that isn't callable, this name is checked for and called.
  • _q_lookup: if an attribute isn't found, this name is checked for and called with the attribute.

This example handles URLs such as /whatever/1/, .../2/, etc.

def _q_lookup (request, component):
        key = int(component)
    except ValueError: 
        raise TraversalError("URL component is not an integer")
    obj = ... database lookup (key) ...
    if obj is None:
        raise TraversalError("No such object.")
    # Traversal will continue with the ObjectUI instance
    return ObjectUI(obj)
  • _q_access: at every step, this name is checked for and called to perform access checks.

_q_access is always called before traversing any further.This example requires that all users must be logged in.

from quixote.errors import AccessError, TraversalError

def _q_access (request):
    if request.session.user is None:
         raise AccessError("You must be logged in.")

    # exits quietly if nothing is wrong

def _q_index [html] (request):
    """Here is some security-critical material ..."""

_q_access is used to impose an access control condition on an entire object; this saves the user from having to add access control checks to each attribute and running the risk of forgetting one. At every step of traversal, _q_access is checked for and called if present. The function can raise an exception to abort further traversal; if no exception is raised, any return value is ignored.

  • _q_resolve: like a memoized version of _q_lookup (rarely used)

HTTPRequest class

  • .response -- a HTTPResponse instance
  • .session -- a Session instance
  • request.get_environ('SERVER_PORT', 80)

-- various standard CGI environment vars

  • request.get_form_var('user') -- get form variables
  • request.get_cookie('session') -- get cookie values
  • request.get_url(n=1)

-- get URL of request, chopping off n pieces

  • request.get_accepted_types()

-- get a dict mapping {MIME type: quality value}

  • browser, version = request.guess_browser_version()
  • return request.redirect('../../catalog')

-- redirect to the specified URL

HTTPResponse class

  • .headers -- dict of HTTP headers
  • .cache -- number of seconds to cache response
  • .set_content_type('text/plain')

-- specify MIME content type of the response

  • .set_cookie('session', '12345', path='/')

-- return a Set-Cookie header to the client

  • .expire_cookie('session')

-- delete cookie from the client

Enabling sessions

Instead of Publisher, use SessionPublisher:

from quixote.publisher import SessionPublisher
app = SessionPublisher('quixote.demo')

The request will then have a .session attribute containing a Session instance.

Two other classes:

  • SessionManager -- stores/retrieves sessions
  • Session -- can hold .user attribute


Running a server on localhost is really easy:

import os, time 
from quixote.server import medusa_http 

if __name__ == '__main__': 
    s = medusa_http.Server('quixote.demo', port=8000)