[DOC-SIG] SGML for Python Documentation

Paul Prescod papresco@technologist.com
Thu, 13 Nov 1997 21:15:13 -0500


I spent the evening on a miniature version of the process of moving the
Python library docs into SGML. I think this will demonstrate SGML's
suitability to the task. The actual document I moved over is a subset of
the TIM document posted yesterday (using ILU and Python). In particular
I:

 * Whipped up a mini-DTD that blended DocBook and the ILU special
abstractions ("metavar", "language", etc.)
 * Encoded some of the TIM docs in my new SGML-based language
 * Wrote a quickie mapping from the ILU abstractions to built-in DocBook
elements
 * Ran the result through Norm Walsh's DocBook DSSSL stylesheets for
print and HTML
 * Loaded the resulting RTF file into Word
 * Made a PostScript file (warning -- Word PS files are "funny" -- I
wouldn't copy them directly to a PS printer if I were you)

All of this was done with free tools except for making the PostScript
file. Theoretically I could have also done that with Microsoft's free
"RTF Viewer" or with TeX. All of the source and result files are at:
http://itrc.uwaterloo.ca/~papresco/ilusgml.zip. All you need to run the
stylesheets is Jade, from http://www.jclark.com/jade. It compiles easily
on every platform I have tried.

I think that the resulting PostScript and HTML files are beautiful. If
there is some aspect that is not beautiful, we can upgrade the
stylesheets quite easily. The complete source is in the zip file on my
website.

I include the contents of the SGML in this email because people on the
list were curious what SGML/DocBook looks like. I could have actually
made the SGML much smaller, but I stuck to a very simple subset of SGML.
I suspect it is the exact subset that sgmllib.py can parse, so it is
already "Python compatible". Were I to use completely idiomatic SGML, I
could reduce the markup by quite a bit, but then sgmllib.py would not be
able to parse it anymore.

I believe that I have thus refuted the arguments that SGML is verbose,
too hard to parse, too expensive and otherwise not appropriate for the
task.

 Paul Prescod

<!DOCTYPE BOOK SYSTEM "ilubook.dtd">
<book>
    <title>Using ILU with Python:  A Tutorial</>

  <bookinfo>
    <author>Bill Janssen</author>
    <copyright><year>1995 </> <holder>Xerox Corporation</></>
  </bookinfo>

<chapter>
    <title>Introduction</>

<para>This tutorial will show how to use the <system>ILU</> <system/ILU/
system with the programming language <language/python/, both as a way of
developing software libraries, and as a way of building distributed
systems. In an extended example, we'll build an <system/ILU/ module that
implements a simple four-function calculator, capable of addition,
subtraction, multiplication, and division.  It will signal an error if
the user attempts to divide by zero.  The example demonstrates how to
specify the interface for the module; how to implement the module in
<language/python/; how to use that implementation as a simple library;
how to
provide the module as a remote service;  how to write a client of that
remote service; and how to use subtyping to extend an object type and
provide different versions of a module.  We'll also demonstrate how to
use <language/OMG IDL/ with <system/ILU/, and discuss the notion of
network
garbage collection.</>

<para>Each of the programs and files referenced in this tutorial is
available as a complete program in a separate appendix to this
document; parts of programs are quoted in the text of the tutorial.</>
</chapter>

<chapter>
    <title>Specifying the Interface</>
<para>Our first task is to specify more exactly what it is we're
trying to provide.  A typical four-function calculator lets a user
enter a value, then press an operation key, either +, -, /, or *, then
enter another number, then press = to actually have the operation
happen.  There's usually a CLEAR button to press to reset the state of
the calculator.  We want to provide something like that.</>

<para>We'll recast this a bit more formally as the <FirstTerm/interface/
of our module; that is, the way the module will
appear to clients of its functionality.  The interface
typically describes a number of function calls which can be
made into the module, listing their arguments and return types,
and describing their effects.  <system/ILU/ uses
<FirstTerm/object-oriented/
interfaces, in which the functions in the interface are grouped
into sets, each of which applies to an <FirstTerm/object type/.  These
functions are called <FirstTerm/methods/.</>

<para>For example, we can think of the calculator as an object type,
with several methods:  Add, Subtract, Multiply, Divide, Clear, etc.
<system/ILU/ provides a standard notation to write this down with,
called <FirstTerm/ISL/ (which stands for ``Interface Specification
Language'').
<language/ISL/ is a declarative language which can be processed
by computer programs.  It allows you to define object types (with
methods),
other non-object types, exceptions, and constants.</>

<para>The interface for our calculator would be written in ISL as:</>

<programlisting>
INTERFACE Tutorial;

EXCEPTION DivideByZero;

TYPE Calculator = OBJECT
  METHODS
    SetValue (v : REAL),
    GetValue () : REAL,
    Add (v : REAL),
    Subtract (v : REAL),
    Multiply (v : REAL),
    Divide (v : REAL) RAISES DivideByZero END
  END;
</programlisting>

<para>This defines an interface <isl/Tutorial/, an exception
<isl/DivideByZero/, and an object type <isl/Calculator/.  Let's
consider these one by one. The interface, <isl/Tutorial/, is a way of
grouping a number of type and exception definitions.  This is
important to prevent collisions between names defined by one group and
names defined by another group. For example, suppose two different
people had defined two different object types, with different methods,
but both called <isl/Calculator/! It would be impossible to tell which
calculator was meant.  By defining the <isl/Calculator/ object type
within the scope of the <isl/Tutorial/ interface, this confusion can
be avoided.</>

<para>The exception, <isl/DivideByZero/, is a formal name for a
particular
kind of error, division by zero.  Exceptions in <system/ILU/ can specify
an <FirstTerm/exception-value type/, as well, which means that real
errors
of that kind have a value of the exception-value type associated with
them.
This allows the error to contain useful information about why it might
have come about.  However, <isl/DivideByZero/ is a simple exception,
and has no exception-value type defined.  We should note that the full
name of this exception is <isl/Tutorial.DivideByZero/, but for this
tutorial we'll simply call our exceptions and types by their short
name.</>

<para>The object type, <isl/Calculator/ (again, really
<isl/Tutorial.Calculator/),
is a set of six methods.  Two of those methods, <isl/SetValue/ and
<isl/GetValue/, allow us to enter a number into the calculator object,
and ``read'' the number.  Note that <isl/SetValue/ takes a single
argument, <metavar/v/, of type <type/REAL/.  <type/REAL/ is a
built-in <language/ISL/ type, denoting a 64-bit floating point number.
Built-in <language/ISL/ types are things like <type/INTEGER/ (32-bit
signed integer), <type/BYTE/ (8-bit unsigned byte), and <type/CHARACTER/
(16-bit Unicode character).  Other more complicated types are
built up from these simple types using <language/ISL/ <FirstTerm/type
constructors/,
such as <isl/SEQUENCE OF/, <isl/RECORD/, or <isl/ARRAY OF/.</>

<para>Note also that <isl/SetValue/ does not return a value,
and neither do <isl/Add/, <isl/Subtract/, <isl/Multiply/,
or <isl/Divide/.  Rather,
when you want to see what the current value of the calculator
is, you must call <isl/GetValue/, a method which has no arguments,
but which returns a <type/REAL/ value, which is the value of the
calculator object.  This is an arbitrary decision on our part;
we could have written the interface differently, say as</>

<programlisting>
TYPE NotOurCalculator = OBJECT
  METHODS
    SetValue () : REAL,
    Add (v : REAL) : REAL,
    Subtract (v : REAL) : REAL,
    Multiply (v : REAL) : REAL,
    Divide (v : REAL) : REAL RAISES DivideByZero END
  END;
</programlisting>

<para>-- but we didn't.</>

<Para>Our list of methods on <type/Calculator/ is bracketed by the two
keywords <isl/METHODS/ and <isl/END/, and the elements are separated
from each other by commas.  This is pretty standard in <language/ISL/:
elements of a list are separated by commas; the keyword <isl/END/ is
used when an explicit list-end marker is needed (but not when it's not
necessary, as in the list of arguments to a method); the list often
begins with some keyword, like <isl/METHODS/. The <FirstTerm/raises
clause/
(the list of exceptions which a method might raise) of the method
<isl/Divide/ provides another example of a list, this time with only
one member, introduced by the keyword <isl/RAISES/.</>

<para>Another standard feature of <language/ISL/ is separating a name,
like <isl/v/, from a type, like <type/REAL/, with a colon character.
For example, constants are defined with syntax like</>

<programlisting>
CONSTANT Zero : INTEGER = 0;
</>

<para>Definitions, of interface, types, constants, and exceptions, are
terminated with a semicolon.
</>

<para>We should expand our interface a bit by adding more documentation
on what our methods actually do.  We can do this with the
<FirstTerm/docstring/
feature of <language/ISL/, which allows the user to add arbitrary
text to object type definitions and method definitions.  Using
this, we can write</>

<programlisting>
INTERFACE Tutorial;

EXCEPTION DivideByZero
  "this error is signalled if the client of the Calculator calls
the Divide method with a value of 0";

TYPE Calculator = OBJECT
  COLLECTIBLE
  DOCUMENTATION "4-function calculator"
  METHODS
    SetValue (v : REAL) "Set the value of the calculator to `v'",
    GetValue () : REAL  "Return the value of the calculator",
    Add (v : REAL)      "Adds `v' to the calculator's value",
    Subtract (v : REAL) "Subtracts `v' from the calculator's value",
    Multiply (v : REAL) "Multiplies the calculator's value by `v'",
    Divide (v : REAL) RAISES DivideByZero END
      "Divides the calculator's value by `v'"
  END;
</programlisting>

<para>Note that we can use the <isl/DOCUMENTATION/ keyword on object
types to add documentation about the object type, and can simply add
documentation strings to the end of exception and method definitions.
These docstrings are passed on to the <language/python/ docstring
system, so
that they are available at runtime from <language/python/. 
Documentation
strings cannot currently be used for non-object types.</>
</chapter>
</book>

_______________
DOC-SIG  - SIG for the Python Documentation Project

send messages to: doc-sig@python.org
administrivia to: doc-sig-request@python.org
_______________