Chapter 4. Sulfur Applications

In some object-oriented languages (Java, for example) the standard class library includes application services in the form of a class. To create an application, the programmer simply subclasses the standard application class and extends it with his own code. The application class takes care of all of the system-specific startup and shutdown activity that a typical program goes through. (This is especially important in the case of special application types, like Java applets which have to interact with a web browser.) Sulfur follows the same pattern.

A Sulfur application is a class derived from Sulfur.Application. All of Sulfur's application-level services are accessed through the methods on the Sulfur.Application class, so it is difficult and perhaps pointless to use Sulfur without using the Application class interface. The simplest Sulfur application looks something like this:

Example 4-1. Simple Sulfur Application

import Sulfur

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

if __name__ == '__main__': Do the following if run as a script
    a = MyApp() Instantiate a MyApp object...
    a() ... and run it.
    

This example application illustrates three fundamental points about Sulfur applications. First, a Sulfur application is a class, derived from Sulfur.Application. Second, the entry point for your code is the run method. Third, running a Sulfur application consists of making an object then calling that object.

Of course, this example doesn't do much. A more complex application will need to make use of the services Sulfur provides, most of which are accessed by calling methods or setting attributes on the application object itself.

Creating a Sulfur Application

In order to create a useful Sulfur application, you need to customize the standard Application object behavior.

Applications as Plug-Ins

Each Application object is also a plug-in. Applications are plug-ins not because they are likely to put in collections and loaded by other applications, but because the plug-in interface provides a number of useful capabilities. (Option handling is the primary one of interest.) So, all Applications should have the usual set of class attributes, as described in Chapter 2. At minimum, you should set the name, version, author, and description fields, since they are likely to be used in help text. Applications may also have an additional special class attribute, extra_help, which may be printed after the automatically generated help text.

Inserting Your Code

The run method, normally empty, is the entry point for your code. For your Application to be at all useful, you must override run with your own code. The arguments passed to run are exactly those passed when the Application object is called (plus the usual self as the first argument).

There is an additional method you can override: get_config. (It takes no arguments, other than self.) The get_config method is called right before run, and is the ideal place to load a configuration file or two.[1]

Options

The Application class includes methods which parse and display help about command-line options. In the future, they may be expanded to support GUI configuration as well. In order to make use of these functions, you must define all of your desired runtime options just like you would define options on a plug-in. That is, you must place a list of Option objects in your Application subclass' option attribute. (The base Application class defines a boolean option called help; the rest are up to you.)

If your Application object has the attribute auto_cmd_line and it is true, Sulfur will parse the command line after calling get_config, but before calling your run method; it will then pass any remaining unparsed arguments to run as the first parameter. If you don't want this automatic behavior, set auto_cmd_line to 0, and call the process_options method:

process_options (argv, extra_plugins)

The process_options method builds a list of valid options based on the current Application object, then parses the command line passed as argv and sets the Application's option values based on what it finds. Anything left over is returned from process_options as a list.

The optional extra_plugins parameter is used to add other plug-ins to the option parsing process. If you supply a list of plug-in objects as this parameter, their options will be considered as the command line is parsed. (Of course, the specified values, if any, will be placed in the proper plug-in.)

The standard Application object defines an option called help, and since options are inherited your Application object has it too. If your Application object has an attribute called auto_cmd_help and it is true (the default), help text will automatically be produced if the user gives this option (as --help or -h) on the command line.

Note: When Sulfur automatically prints help text, it does not cause your application to stop running. If you want to do that, you must test the help option for a true value and act accordingly.

If you don't want the default behavior, set auto_cmd_help to 0, and call process_help when you want to present help text to the user. As with process_options, you can call process_help with a list of extra plug-ins from which to build the help text.

Plug-In Services

The Application class provides several methods for finding and loading plug-ins:

get_plugin (collection, name)

Finds, loads, and instantiates a plug-in.

list_plugins (collection)

Returns a list of all available plug-ins in the specified collection.

list_plugin_info (collection)

Gets information about all available plug-ins in the specified collection. The return value is a dictionary, keyed by plug-in name; each value in the dictionary is also a dictionary, containing the values of the most commonly used plug-in class attributes.

Notes

[1]

You're probably asking yourself "why not just do that at the beginning of run?". The reason get_config exists is subclassing: if you want a broad class of applications to use the same configuration setup, it is better to put it in one place than to make every application copy it into their run method.