Version 2.1: 3 December 2008

Make qp operable in both python 2 and python 3.

Run the qp script through a __main__.py module.

Add Form.clear() and widget.clear() to allow clearing values from submitted 
forms.  (Contributed by David Hess).

Fix digest authentication for IE 7.  Thanks David Hess.

Use a class attribute, MAX_TOKENS, to hold the limit on the number of
retained form tokens.

Convert qp's templates to use qpy's new :xml syntax.

Remove check of REQUEST_URI when hacking path_info for mod_scgi.

Add an ImageButtonWidget to QP

Add remove method to keep
Add __len__ method to keep
Add 'mapping_class' keyword to Keep constructor that defaults to BTree.

Add some Counter variant classes in the keep module.
Add Keep.set_counter().
Retry if necessary to find a key that is not already taken when
adding a new item to a Keep.

Add rand_int().  Add a unit test for the random utils.

Drop support for calling get_path() with an integer argument.

In request.py:
Remove get_content_type().
Remove parse_content_disposition().
Simplify Upload.__init__().

Add "integer" to spec module.

Add rand_str() to qp's util module.
Note that a str is a different thing in python2 and py3k,
and that is why these utilities are useful.

Require Python 2.4 or later.  Remove randbytes() implementations that 
were needed for earlier versions of Python.
  
This version of QP requires version 1.7 (or later) of QPy.

Remove use of pyrepl.  Just use the standard interaction.

Add support for a "gcbytes" configuration value, which controls
the frequency of automatic garbage collection.

Version 2.0: 2 May 2007

Use PersistentObject instead of Persistent as the base class of
Session, User, Permissions, Tokenset, and Digester.  Change these
classes to use slots for data attributes.

Change Form.has_errors() so that it forces all
widgets to be parsed before checking to see if
any of them have errors.  

Change the wording of the form error notice.

If a composite widget's name is among the request fields, then
is_submitted() should return True.  In this case, we would normally
expect the composite widget to have a customized _parse() method.

Add a get_element_widgets accessor to WidgetList.

Change the ButtonWidget parsing so that button presses are detected
even if the label has been changed.

Add an update_db_environment() method to Site.

Fix persistence error in ungrant().

Add a DEBUG attribute to the SCGIHandler class.
If true, the received scgi headers will be logged.

Add encode_header() function (that depends on the email package)
and use it to support encoded real names in addresses, and 
encoded subject lines.

When we set the body of an email message, if the body is a unicode, 
encode it as utf8.

If you actually try to send mail and it fails, raise an exception.

Add the number of bytes read to the message of the exception that
is raised when the request body is shorter than the expected length.

Change the qp script so that it does not place QP=1 in the environment.

To qp.pub.common, add get_connection, get_config_value, get_users, and
complete_path.

Factor complete_path() out of complete_url() on the publisher.

Make the default StaticDirectory skip '.htaccess' files.

Add follow_links keyword to StaticDirectory.

Allow StaticFiles to work through symlinks.

Handle banned addresses earlier.

getpeername() is no good for identifying remote addresses with scgi.
This update uses recv with the MSG_PEEK flag to look into the headers
of the scgi request to try to find a REMOTE_ADDR value.  If present,
the remote address is used to decide which child should get the
request.

If the busy_limit has been reached for a connection's IP address, the
connection is left in the queue.  This prevents rapid bursts of
requests from a single IP address from shutting out requests from
other IP addresses (when using scgi).  Support for X_FORWARDED_FOR is
present but not tested.  The intent is to provide the same selective
delegation when the qp http server us running through a proxy.

Add busy_limit as a configuration option.  The default value is 1.

In the dispatcher, if no explicit header is found, use the peer 
address. If the address is localhost or not found, just give
the request to the first available child.

Revise dispatcher code.
Peek into headers to try to find the correct remote address.
Use the address to direct requests back to a child that just
processed a request from that address.
Add a configuration variable, 'busy_limit', that limits the
number of child processes that the dispatcher will knowingly
allow to be busy handling requests from a single IP address.
This helps protect against flurries of requests from a single
address, thus keeping the server available for requests from 
other addresses.  
Log more about what is happening in the dispatcher process.

Expose a little more of the exception if a site import fails.

Add a monospace_textarea css class to support code-like input 
widgets.

Use "class" and "css_class" arguments, given to the widget constructor,
as additional css class names.
Add "CompositeWidget" as a class name for all instances of CompositeWidget.
Add a css class "subwidgets_float_left" that makes it easy to add
widgets with left-floating subwidgets.

Fix bug in Checkbox widget parsing.  Add tests.

Set the default durus_cache_size to 500k.
Use this configuration value to set the cache size.

Add Publisher.is_hub(), which tells if the publisher was created from 
qp's scgi and/or http server.  This may be a factor in determining
whether or not email is enabled.  For example, test scripts may make
a publisher instance, and in this case you probably don't want email 
to be enabled.

Use decimal instead of hex for formatting oids as keys for SelectWidget.

Change the way that the qp script handles arguments.
All arguments are now of the "store_true" variety, and the sites
involved are always provided as stand-alone arguments.
For flags like -l and -i, the site is always assumed to be the
last one among the named sites.

Fix argument error in datetime.now() call in the site_now() function.  

Allow the session attribute of the Hit instance to be None, but
implement get_session() so that it creates a new Session if the
value is None.  This allows a DurusPublisher() to generate normal
error responses (that may expect a session to be present) when
the request input processing fails.

Add css fix for IE Peek-a-boo bug also known as IE buggy box

Modify persistent_vars() so that it also works for non-persistent instances
that use slots for data attributes.

Factor out a process_inputs() method and put the call inside
a "try" statement that catches RespondNow exceptions.
Implement process_inputs() so that bad requests log the
traceback and return a status 400 response.

Log the exception rather than the entire traceback if we can't
parse the request.

Mixin classes were forcing their subclasses to have __dicts__, but we
wanted the possibility of having subclasses with slotted attributes.
We added a new Mixin class with a new metaclass named SlottedType.
A class whose metaclass is SlottedType has a __slots__ class attribute.
Basically, classes whose metaclass is Mixin get __slots__ = [], and
classes whose metaclass is Specified get __slots__ values that list the
names of the specified attributes (the "*_is" attributes).
The bottom line is this:  classes that are intended as mixins for 
persistent classes can have Mixin as a base, and when this pattern
is followed, persistent classes that use mixins can have slotted attributes.

Remove duplicate call to log_hit in process_hit when a RespondNow
exception is raised when trying to handle the request.

Make timestamps on the Stamp class be timezone-aware.

Add a __slots__ = [] line to mixins that will mix with PeristentObject
classes so that the subclasses have the option of using slots for 
attributes.

Change implementation of compute_hash so that unicode
instances are automatically encoded as utf8 before 
computing the hash.

Make PersistentObject the base class of Counter.

Treat extra arguments to qpcensus.py as site names.

Add --shadows flag to qpcensus.py.  If given, the script reports class attributes
whose value depends on  base class order.  We try to avoid that.

Always unlink pid files when servers are stopped.
Catch unlink exceptions.

Make qp's "start" argument attempt to stop the current server first.
This also clears any pidfiles that are left after an abnormal termination.

Used parseaddr from the Python email package to try splitting
the addr_spec arg into a real_name and an addr_spec.  If a 
non-empty real_name is able to be parsed out of the addr_spec
use it rather than any value that was passed into the constructor. 

Add Publisher.get_agent_class() method, which returns a short identifier
of the user agent.  This identifier is used as a css class for the
BODY element, which gives an easy way to define agent-specific css
without exploiting specific browser bugs.

Version 1.9.1: 20 November 2006

Fix bug in parsing of submit widgets.

Add another submit widget to proto forms demo.

Make DurusDirectory work with slotted persistent objects.

Make Session.owner spec more direct.

Remove repeated "the" from form token notice.

Simplify qp script.  
Avoid imports of sites that are not used.
Remove --site option.  Just give the site name as an argument.
Note that "start", "stop", and "restart" are reserved, so don't
use those as site names.
Remove --build option.
Remove --base option.

Note about time zones.  Release 1.9 changed Session times to
be time zone aware.  If you are upgrading an existing QP
application to 1.9 or 1.9.1, you should open your database
in an interactive session, remove all sessions with
"sessions.clear()" and commit().  This will terminate
all open sessions, but the newer sessions will have the
time zone aware timestamps.  If you want to work harder,
you can replace the timestamps on your current sessions
with time zone aware versions.

Version 1.9: 15 November 2006

Revise the qp script to provide more flexibility in where site
packages are found.
Run qp --help for details.

Add lookup_user() and add_user() methods to the DurusPublisher.

Add Site.make_file_connection() for use by database update scripts.

Add a trace() decorator. (Useful in database update scripts.)

Change semantics of form.is_submitted to be true if any widget
name appears in the fields of the request.  If the form has 
been submitted, form.is_submitted *modifies* the request fields 
as necessary to make sure that every widget of the form has a 
key in the fields dictionary.

Add Site.get_live_host_name().

Add Publisher.is_live_host().
Don't send exception email except on the live host.
Suppress exception display on the live host.
Don't provide a debug address when on the live host.

Make interaction work when there is no durus server running yet,
and for sites that have no durus server.

Use basename of path as the index crumb for a static directory.

Always add a Content-type line to email.  (Mario)

Verify the operation of the Publisher constructor before forking.

Add a qp.lib.tz, a module with classes that are tzinfo instances
for 4 US timezones, UTC, and fixed offsets from UTC.

Add Publisher.get_time_zone(), which returns the site's default.
The default implementation uses UTC.
Add site_now(), which returns the current time with the Publisher's
time zone.  Use timezone-aware times on sessions.

Add datetime_with_tz and datetime_without_tz specs.

If a FileWidget is added, make sure the Form's enctype is correct.

Add --showmax option to qpcensus.py.  If the option is on, the
output includes a string that describes the location of an instance 
of maximal size for each class.

Update passfd.c to the scgi 1.11 version, plus the Solaris 8 patch
posted by James McDonald to quixote-users.

Add Specified, a class with a metaclass that makes a __slots__ from
the *_is attributes, including those from base classes.

Add get_memory_usage(), a utility that uses ps to return the
resident and virtual memory usage of the current process, in
units of 1024 bytes.  (uses "ps").

Add persistent_vars(), which works like vars(), except that it also
works on persistent instances that use slots instead of __dict__.

Add a --showpath option to qpcensus.py.  By default, just show 
oids instead of a fancy path.  This is faster and reduces memory
requirements by about 50%.

Reduce loading required by qpcensus.py.

Pass keyword arguments through make_file_connection to the FileStorage
constructor, in case a readonly storage is desired.

Add Form.__iter__() and Widget.__iter__()
and use them in the Form methods that should
also deal with subwidgets.  Previously, these
methods used Form.get_all_widgets(), which 
does not traverse down into subwidgets.

Version 1.8: 22 August 2006

Simplify Site.get_durus_address().

Add a delegation module.  This provides delegate(), a method for
adding methods to a class that delegate to methods on instance
attributes.

Version 1.7: 16 August 2006

Add set support to the spec module.  A spec that is a set matches
a set whose elements match any element of the spec.  For example,
set([int]) matches set([2,3,5]), but not set([2, 'S']).
set([int, str]) matches set([2, 'S']), but not set([2.0, 'S']).

Change the implementation of the "anything" spec so that it works
properly when used directly as a class attribute.

Make run_durus() defer to a Publisher method so that it can
be customized more easily.

Don't sleep when the server is already dead.

Add a "prefix" keyword argument to the Form  constructor.
If a prefix is provided, use it as a prefix for widget internal names.

Add is_submitted() to widgets, and use it in Form.is_submitted().

Add Profile, a class for profiling the processing of a request.

Make -u stop the durus server, since -q is provided for quick restart.

Remove HUP handling and do_restart() from dispatcher.
Add quick restart option to qp that sends a HUP to the
web dispatcher process of a particular named site.
Modify web server so that it execs new processes 
but recycles the bound sockets, whose file numbers
are made available to the new process through an
environment variable.

Add a line to the hello page that reports the time that this
python module was loaded.

Attempt to parse form values from querystrings that have html quoted
ampersands.

Version 1.6: 18 May 2006

    Revise get_crumb_tree() to provide css_class information.
    
    Change the widget subname divider from $ to __ so that the widget name
    is a valid value for an HTMl id.
    
    Revise form rendering and css so that we can produce forms with the
    same appearance as our Quixote-based forms.  Add an empty
    '<div class="close"></div>' at the end of sections that might 
    commonly enclose floating elements.  This gives non-floating 
    containers a chance of enclosing floating components.
    Insert newlines in lots of places in form rendering so that the
    raw source is easier to read.
    
    When the qp site management tool runs, put QP=1 in the environment.
    
    Make the HTTP server provide the correct Server: header in responses.
    
    Delay calling root directory constructor so that publisher subclass
    constructors can avoid initialization circuits.
    
    Add support for using durus through a unix domain socket.
    
    Add hello and hello_user exports to the proto demo.  Remove
    authentication from the demo's "request" page.
    
    Revise readme notes about stunnel.
    
    Add apache notes to the readme.
    
    Remove name="" from the noscript input tag rendered for OptionSelectWidget.
    
    Change the behavior of the publisher when PATH_INFO is the empty string.
    
    Change access log format.  Add access log line parsing method to the
    publisher.
    
    Make site start_web() defer to the publisher for the details of launching
    the web server(s).  This should make it possible to customize the web 
    servers on a per-site basis.  This tricky customization should not, 
    however, be undertaken unless there is a compelling reason for it.
    Note that Publisher.run_web is a staticmethod so that it can be called
    before there is a publisher instance, which is very desirable if you want
    child processes to run with separate connections to the Durus server.
    
    Separate start_durus_logging() method so that it can be used by clients.
    
    Add a javascript callback demo to proto.
    
    Put the parameters of spec's format_expected_got() in the right order.
    
    Initialize tmpbuf contents in passfd.  Jonathan Corbet posted this as a
    fix for trouble on an x86-64bit machine.
    
    Move the symlink test to the __call__ of StaticFile.  The not_found() call
    can't operate until there is a Publisher.  If the file is not found,
    just return with not_found() instead of a traceback.
    
    Change spec matching algorithm so that, if True or False appears in a
    spec tuple, it is treated as an OR.
    
    Use a definite site, instead of a random one, for testing Publishers.
    
    Don't do anything in send_mail_to_administrator() if is_email_enabled()
    returns False.
    
    If process_hit() is called with a PATH_INFO that  does not start with a
    slash, the response is now a 500.  Note that Publisher.process() provides
    the SCRIPT_NAME -> PATH_INFO hack that is required by some SCGI clients.
    
    Factor generate_visibile_names() from the get_exports() of StaticDirectory.
    Override this if you want to exclude certain patterns.  The default is
    to exclude ".svn" and names that end in "~".
    
    Include a static directory in the proto demo.
    
    Rename qp.web.util to scgi_util and make it usable as a script for testing 
    an scgi server.
    
    Make complete_url() use the hostname from the request if the hostname in
    the https_address is empty.
    
    Make url_with_query call stringify on keys and values provided for the
    query.
    
    Fix default Publisher.header() so that it can't put style outside of the
    head.
    
    Always change the owner of the session to the authenticated user, not just
    if there is no previous owner, when set_authenticated() is called.
    
    Make sure that the rows of list and dict widgets appear in rows.
    
    When the client rapid-fires requests and closes the connection before 
    getting the response, the web server was crashing with a socket.err.  The
    web server now catches these errors so that the server can keep running.
    
    Add an --all flag to qpcensus to cause it to report all spec errors for a
    given class, instead of just the first one found.
    
    Fix a bug in _generate_keys() that caused bad keys for SelectWidgets with 
    non-Persistent values().
    
    Make Form._get_default_action() return the value it computes.
    
    Don't quote the ampersand in url_with_query().
    
    Add a Redirector to qp.  This allows _q_lookups to consume the remainder
    of the path and redirect.
    
Version 1.5: 15 March 2006

    Separate Publisher class by pushing the Durus-using methods into a
    DurusPublisher subclass.  This clears the path for using QP for applications
    that use other databases, and for those that use no database at all.

    Obtain "SitePublisher" from the module named "slash" in the site
    directory instead of from the site package itself.  The extra
    redirection seemed pointless.
    
    Obtain "SiteDirectory" from the module named "slash" in the site
    directory instead of getting "SiteRootDirectory" from the site package 
    itself.   If you have an existing QP site, make sure that the root
    directory class is qp.sites.<site_name>.slash.SiteDirectory .
    Because some sites may not use Directory-based traversal at all,
    the SiteDirectory is now optional.
    
    Change the "hello" demo to show how to make an application that does
    not use any of the normal QP traversal or page generation patterns.

    Change the "echo" demo to be a Durus-less QP application.
    
    Store the global publisher in a dictionary by thread id.  This should
    make it easier for people to experiment with using a QP publisher in
    a multi-threaded server.  
    
    Add a __call__ to the Publisher class so that a Publisher instance can
    act as a wsgi application.  This should make it easier for people to
    try using a Publisher within a wsgi server.
    
    Make the core QP server class usable when passfd is not available.  The
    server reverts to a non-forking mode, so the performance is expected to
    be lower.  This should, however, make it possible for people to use QP on 
    Windows machines.
    

Version 1.4: 2 February 2006

    Add an email field to the qp user.
    Specify the email and id attributes using a pattern.
    
    Add charset and mime_type options to sendmail options and 
    use them to add a Content-Type header.

    Add access_time recording to qp sessions.
    
    Remove side-effect from is_valid(), the caller takes care of this.
    
    Add "proper" spec operator so that we can say something like
    [proper(Answer)] to specify a list of spec-correct Answer instances.
    
    Remove cruft from is_granted() that Mario Ruggier found.

    Allow calls to footer to have non-keyword arguments.
      
Version 1.3: 13 December 2005

	Add form demo to proto site.
	
	Add add_javascript_code() and get_javascript_code()  utilities to the form
	module.
	
	Simplify basic form css.  Change form widget rendering to conform to 
	xhtml-strict.  Remove render_br keyword option.
	
	Add DOCTYPE to the default header.
	
	Add style and doctype keyword options to header().	

	Add HTTPRequest.get_content_type().
	
	Use "User Name" instead of "Email" as the widget title on the default login
	form.
	
	Use / instead of "" for the path, when there is no script name.

	Change secure() so that it does not raise an exception when there is no
	https_address.
	
	Set site in Publisher.__init__() before calling 
	ensure_database_initialized(), (formerly named ensure_initialized()).
	
	In redirect(), use the existing response instead of making a new one.
	This allows cookie headers to be part of a redirect response.
	
	In DurusDirectory._q_lookup, call self.__class__ instead of DurusDirectory.
	
Version 1.2: 4 November 2005

	Add read_body() and get_content_length() methods to the request class.

Version 1.1: 2 November 2005
    
    Change request default charset from 'iso-8859-1' to 'utf-8'.  In qp, html
    pages will use utf-8 encoding, so we will assume, in the absence of other 
    information, that requests use utf-8.  
    
    Allow for sessions when there is no https support.
    
    Remove Persistent from the __bases__ of Keyed and Keep so that they can
    be used as mixins with classes that already inherit from Persistent.


Version 1.0: 20 October 2005
    
    First release.

