This directory contains Sulfur, the Pyrite application framework.  It
has been separated from the rest of Pyrite because it is of general
usefulness.  Eventually, it will be released as a separate product.

Here is an overview of Sulfur; the beginnings of a programming guide
can be found in the 'doc' subdirectory.

As I have worked on Pyrite, I discovered that I have been building an
application structure which may be useful outside of the context of
Palm-oriented applications.  In fact, I find more and more of my other
Python projects importing pieces of PDA.Palm.* even though they have
nothing to do with the Palm.  Sulfur is an attempt to separate out
this stuff, so that eventually it can be used by other applications
without having a dependency on Pyrite.

Sulfur provides services to applications, under the assumption that an
"application" is something the user runs, rather than a library of
Python code which is intended only for use by other code.  (Actually,
given the object oriented nature of Python, an application is BOTH,
but that's a matter for later discussion.)  Some of these services
include: configuration file reading, plug-in loading, flexible option
handling, etc.

* Applications:

In some other object oriented languages & libraries, there is a
standard "Application" class which all user-level applications derive
a subclass from.  Sulfur follows that same model: a Sulfur application
is just a customized subclass of "Application"; the simplest Sulfur
application is something like this:

    import Sulfur

    class MyApp(Sulfur.Application):
        def run(self, *a, **kw):
	    print "hello world"

To run it, you merely instantiate a "MyApp" object, and call that
object:

    a = MyApp()
    a()

The rest of Sulfur is accessed by doing stuff to the Application
object, either by calling its methods or setting attributes.  Most of
the Application class' methods are intended to be used by the user's
own code, as an API to the rest of Sulfur.

* Plug-Ins:

At run-time, a plug-in is an object.  All plug-ins follow a common
protocol, and can have additional programmer-defined behavior in order
to do something useful.  Sulfur provides facilities to find and load
the Python code that defines a plug-in.

Plug-ins are organized into "collections"; a "collection" is basically
just the name of a Python package.  When your code requests a plug-in
by name, Sulfur looks for the appropriate module.  For example, if you
ask for a plug-in called 'Backup' in a collection called 'Conduit',
the end result is more or less the same as "import Conduit.Backup".
It is not quite the same, however, because Sulfur will actually try to
find the collection and the conduit within along a package path; this
avoids the "one package named <foo>" limitation of the normal Python
package system.  (The idea of this is that several different packages
can provide plug-ins in the same collection, and the path search will
make sure they can all be found.)

Once Sulfur imports the plug-in's module, it looks inside for a class
that follows the plug-in protocol, and if it finds one, it
instantiates the plug-in object and caches it for later use.  (If you
need more than one of something, make the plug-in object a factory
that generates additional objects as needed.  Pyrite's Stores are an
example of this.)

Application objects follow the plug-in protocol; this is mostly so
that they can have automatically configured options, but it could also
prove useful in building composite applications...

* Options:

Every plug-in can have a list of options.  An Option object is a
container for the definition of an option (name, type, and so on) as
well as its current value.  There is some support for validation,
which causes an exception to be raised if something tries to set an
option to an invalid value.

The list of options is used for autoconfiguration: after a plug-in is
loaded, its option values are set from the configuration registry (see
below).  In the future, Sulfur will also support generic option
configuration dialogs in Tkinter or possibly other GUI systems.

Applications can also have their options set from the command-line;
the Application class includes getopt-like functionality which uses
the list of options already defined, and which can also produce help
text from option definitions.

* Configuration Registry:

Sulfur reads option information out of configuration files in the
typical INI format; that is, section names in brackets and option
names one per line with values after an '='.  Sulfur includes a
"Registry" object which acts basically as a double-key (section and
option name) dictionary, and which can get its contents from an
overlaid stack of configuration files.

