Go to the previous, next section.

# Using ILU with Python

## Introduction

This document is for the Python programmer who wishes to use ILU. The following sections will show how ILU is mapped into Python constructs and how both Python clients and servers are generated and built.

## The ISL Mapping to Python

### Names

In general, ILU constructs Python symbols from ISL names by replacing hyphens with underscores. For example, an ISL object type `T-1` would correspond to the Python class `T_1`. Any place an ISL name appears as part or all of a Python identifier, this translation occurs.

### Interface

Each ISL `interface` `I` generates two Python modules: one named `I` containing common definitions, and another named `I__skel` containing skeletons (server stubs). For example, `INTERFACE map-test;` generates the Python modules `map_test` and `map_test__skel`, contained in the files ``map_test.py'` and ``map_test__skel.py'`, respectively.

### Constant

ISL constants translate to Python variables initialized to the specified value. For example,

    CONSTANT pi : real = 3.14159265358979323846;

maps to

    pi = 3.14159265358979323846e0

### Exception

An ISL exception translates to a Python variable initialized with a string representing the exception. These variables are used in Python `raise` statements in object implementation code, and in `try ... except` statements in client code. For example, the declaration

    EXCEPTION division-by-zero;

in the interface `map-test` maps to the following statement in ``map_test.py'`:

    division = 'map-test: division-by-zero'

### Types

#### Basic Types

The basic ISL types have the following mapping to Python types:

1. BYTE, BOOLEAN, SHORT CHARACTER, CHARACTER, SHORT INTEGER, INTEGER, and SHORT CARDINAL all map to Python int.
2. LONG INTEGER, CARDINAL, and LONG CARDINAL all map to Python long int.
3. SHORT REAL and REAL map to Python float.
4. LONG REAL maps to the Python type ilu_longreal, a type implemented by the ILU Python runtime. This type has limited functionality, but can be passed around without loss of precision, converted to float or int, and compared. A value of this type may be constructed by calling `ilu.LongReal()`.

#### Enumeration

Enumerations are mapped to a method-less class object which contains an attribute of the correct type and value for each value in the enumeration. The class also contains a dictionary, called "__image__", for each enumeration type that maps an enumeration value to a string corresponding to its Python enumeration value name.

For example,

    TYPE color = ENUMERATION red, dark-blue END;

maps to

    class color:
        red = 0;
        dark_blue = 1;
        __image__ = {
           red: 'red',
           dark_blue: 'dark_blue'};

#### Array

An ISL array maps into a Python list with the specified number of elements. Tuples as well as lists are accepted as input, but lists are always produced as output from ILU. Arrays of BYTE or SHORT CHARACTER are represented with Python strings.

#### Sequence

An ISL sequence of short character maps into a Python string.

All other ISL sequence types map into Python lists. Tuples as well as lists are accepted as input, but lists are always produced as output from ILU. Sequences of BYTE or SHORT CHARACTER are represented as Python strings.

#### Record

ISL records map into generated Python classes with the same name, with the record's field names as attributes. The name of the record becomes a constructor function which accepts exactly the same number of arguments as the record has fields, in the same order.

For example, a record value of the ISL type

    TYPE segment = RECORD left-limit : integer, right-limit : integer END;

with a left-limit of -3 and a right-limit of 7 would map to

    segment(-3, 7) => <segment:{'left-limit' : -3, 'right-limit' : 7}>

#### Union

An ISL union maps into a Python tuple with two components: an integer discriminator, and the discriminated value. There are three possibilities:

1. If the discriminator matches one of the union case values of an arm, the second component is of the type specified by that arm.
2. If the discriminator matches no union case values and there is a default arm, the second component is of the type specified by the default arm.
3. If the discriminator matches no union case values and there is no default arm but the union has the `OTHERS` attribute, the second component is `None`.

#### Object

Each ISL object type is mapped into a Python class. These classes have the methods specified in the ISL, as well as some built-ins.

#### Optional

A value corresponding to the ISL type `OPTIONAL T` may be `None` (indicating the null case) in addition to the values of the type T.

### Methods and Parameters

ISL methods of an object type map to Python methods of the corresponding class. `IN` and `INOUT` parameters appear in the Python method signature in the same order as they do in ISL.

Let us define a result value to be either a return value (corresponding to a method's return type) or an `INOUT` or `OUT` parameter. Result values are returned by the Python method as a tuple, with the return value (if present) appearing before any parameters. If the method has only one result value, then it is simply returned (i.e., a tuple of length one is not constructed to hold this value). If the method has no result values, then `None` is returned.

### Threads

To use threads, you must have configured both ILU and Python with thread support when building them. If you have done this, your ILU/Python runtime support will be thread-capable. To have ILU begin using threads, place a call to the function `ilu.ThreadedOperation()` in your Python program before any other ILU calls are made.

### Identities

An ILU passport (see See section Security) is represented in Python by a dictionary. Each entry has one of the following keys:

- `"connection"` -- this key is used for a value which is a string containing an identity of type `ilu_ConnectionIdentity`.
- `"sunrpc-unix"` -- this key is used for a value which is itself a dictionary, containing the fields `"uid"`, `"gid"`, `"hostname"`, and `"groups"`.
- `"GSS"` -- this key is used for a value which is a Python GSS object.

The passport may be obtained in the true method by calling the ILU runtime routine `ilu.CallerIdentity()`.

## Using an ILU module from Python

The ILU runtime interface is in the Python module `ilu`. Python definitions for ISL `INTERFACE I` are in the Python module `I`. As with any other modules in Python, these modules are imported using the `import` statement.

A client program may create an ILU object in one of three ways:

1. Knowing the string binding handle `sbh` and class `cl` of an object, call `ilu.ObjectOfSBH(cl, sbh)` which returns an instance of that class. For example, to obtain an instance of ISL type `square` from `INTERFACE shapes` whose string binding handle is `sbh`, one would call `ilu.ObjectOfSBH(shapes.square, sbh)`.
2. Knowing the object ID `(sid, ih)` and class `cl` of an object that has been published using the simple binding service, call `ilu.Lookup(sid, ih, cl)` which returns an instance of that class (or `None` if the lookup fails).
3. Receive an instance as a result value from a method call that returns an object type or has an object type as an `INOUT` or `OUT` parameter.

## Implementing an ILU module in Python

A Python module that implements ILU objects of types defined in `INTERFACE I` also imports from `I__skel`. This gives access to the skeleton classes from which implementation classes inherit.

### Implementation Inheritance

An implementation of object type T from interface I needs to inherit from the class `I__skel.T`. If there is inheritance in the ISL, and an implementation of a subtype wants to inherit from an implementation of a supertype, the skeleton class must be appear in the list of base types before the implementation class.

For example, objects for the ISL

    INTERFACE j;

    TYPE c1 = OBJECT METHODS one() END;
    TYPE c2 = OBJECT METHODS two() END;
    TYPE c3 = OBJECT SUPERTYPES c1, c2 END METHODS three() END;

could be implemented in Python by

    import ilu, j, j__skel

    class c1(j__skel.c1):
        def one(self):
            ...

    class c2(j__skel.c2):
        def two(self):
            ...

    class c3(j__skel.c3, c1, c2):
        def three(self):
            ...

In this case `c3`'s method `one` is implemented by `c1.one` and `c3`'s method `two` is implemented by `c2.two`.

### True Servers

Each object exported by an implementation must belong to a true server, an instance of the Python type `ilu_Server` which is implemented by the ILU runtime. An `ilu_Server` can be created by calling the function `ilu.CreateServer([serverID [, transport [, protocol [, objectTable]]]])`, which returns a value of type `ilu_Server`. If serverID is a string, it specifies the server ID; if it is `None`, one will be invented automatically. The transport and protocol arguments are strings to choose a specific transport or protocol, or `None` to let them default. The objectTable argument allows specification of a callback function for creating true instances on demand. The callback function should take one argument, a string, which is the object ID of the instance to be created, and return a true instance.

The first time a true server is created, it becomes the default server. The default server is used for an exported object if a server is not otherwise specified. If an object is exported before any servers have been created, one will be created automatically using default parameters and a message to that effect will be written to stderr.

An object of type `ilu_Server` has a method `id()` that returns its server ID.

### Exporting Objects

An object can be exported in one of three ways:

1. The object's string binding handle may be obtained by calling its method `IluSBH()` and communicating this somehow to a client, who then turns the handle back into an object by calling `ilu.ObjectOfSBH(cl, sbh)`.
2. The object may be published using the simple binding service by calling its method `IluPublish()`. In order for this to be effective, the object must have a well-known object ID, or the object ID must be communicated to clients, so clients can know what to pass to `ilu.Lookup`. The object ID is a function of the object's instance handle and its server's server ID.
3. The object may be returned by a method or passed back in a method's `INOUT` or `OUT` parameter.

An object's instance handle can be controlled by setting the instance variable `IluInstHandle` before the object is first exported. If this instance variable is not set, and instance handle will be invented automatically.

An object's server can be controlled by setting the instance or class variable `IluServer` to a value of type `ilu_Server`. The value of this variable at the time an object is first exported will be used as the server for that object. If such a variable is not set, the default server is used.

### Animating Servers

Running the ILU main loop by calling `ilu.RunMainLoop()` brings the true servers to life. This function does not return until `ilu.ExitMainLoop()` is called. If you are using ILU with `Tkinter`, run the main loop by calling ``ilu_tk.RunMainLoop()``, rather than using either the ILU or `Tkinter` main loops. ``ilu_tk.RunMainLoop`` sets things up so that both Tk and ILU events are handled.

### Using Alarms

In order to schedule a Python function to be called at a certain time in the future when executing the ILU main loop, an `ilu_Alarm` may be used. Objects of this type are created by calling `ilu.CreateAlarm()`. An `ilu_Alarm` must be set to have any effect.

The alarm's method `set(time, proc, args)` is used to set the alarm. The `int`, `float`, or `ilu_FineTime` `time` argument is the time at which the alarm will fire; the `proc` argument is the Python function that will be called when the alarm fires; and the `args` argument is a tuple of arguments to be passed to `proc`. The tuple `args` must match `proc`'s signature. For example, if `proc` is declared `def P(a, b):` then `args` must be a two-tuple. Likewise, if `proc` takes only one argument then `args` must be a one-tuple, or if no arguments then a zero-tuple.

The function `ilu.FineTime_Now()` may be called to obtain ILU's idea of the current time. A value `sec` of type `int` or `float` in units of seconds may be converted to type `ilu_FineTime` by calling `ilu.FineTime(sec)`. Values of type `ilu_FineTime` may be compared, added, and subtracted. These operations may be used to construct values representing any relative time (subject to precision and range limitations), which is what is needed by an alarm's `set` method.

The alarm may be set multiple times with different arguments, in which case the parameters of the most recent call to `set` are in effect. Thus, once an alarm fires, it may be reused by calling `set` again.

An alarm may be unset by calling its method `unset()`.

## Using the Simple Binding Service

An object may be published using the simple binding service by calling its method `IluPublish()`. An object may be unpublished by calling its method `IluWithdraw()`.

A published ILU object may be obtained by calling `ilu.Lookup(sid, ih, cl)`, where `sid` is object's server's server ID, `ih` is the object's instance handle, and `cl` is its class.

## Summary of the ILU Python Runtime

Exported from module `ilu`:

- `def CallerIdentity():`

  Returns the passport containing identities of the caller. This routine is only valid inside the code of a true method.

- `def CreateAlarm():`

  Creates an object of type `ilu_Alarm`.

- `def CreateLoopHandle():`

  Creates and returns an instance of a "loop handle" object, which can be passed to `ilu.RunMainLoop` and `ilu.ExitMainLoop()`.

- `def CreateServer([serverID [, transport [, protocol [, objtable]]]]):`

  Used to create an `ilu_Server` object with the specified serverID, transport, and protocol. If serverID is unspecified or `None`, an identifier will be invented automatically. If transport or protocol are unspecified or `None`, they will default to `'sunrpcrm|tcp_localhost_0'` and `'sunrpc_'`, respectively. The first time ``CreateServer`` is called, the server so created becomes the default server. If there is no default server when one is required, one will be created using default parameters and a message will be issued on stderr. The objtable argument allows specification of a callback function for creating true instances on demand. The callback function should take one argument, a string, which is the object ID of the instance to be created, and return a true instance.

  An `ilu_Server` object has an `id` method which returns the string identifier of that server.

- `def DefaultServer():`

  Returns the default server.

- `def Delete(obj):`

  Puts the specified object on a list to schedule it for eventual deletion. Actually, the deletion routine simply decrements the reference count increment used by ILU to hold onto an object, so if other references to the object exist, the object will continue to exist.

- `def ExitMainLoop(loophandle):`

  Exits the ILU main loop, assuming it is running. The loophandle is created by a call to `ilu.CreateLoopHandle()`, and must have been previously used as an argument to a call to `ilu.RunMainLoop()`.

- `FALSE = 0`
- `def FineTime(sec):`

  Converts its `int` or `float` argument sec in units of seconds to type `ilu_FineTime`. Objects of this type can be compared, added, subtracted, and converted to `int` or `float`. The main use of objects of this type is in setting alarms.

- `FineTimeRate = ...`

  The precision of type `ilu_FineTime` in seconds is the reciprocal of this constant.

- `def FineTime_Now():`

  Returns the current time as an `ilu_FineTime` object.

- `def FormSBH(objectID, contactInfo):`

  Returns the string binding handle corresponding to the object id objectID and contact info contactInfo. This is the inverse of ``ParseSBH``.

- `IluGeneralError`, `IluProtocolError`, and `IluUnimplementedMethodError` are all strings that may occur as exceptions from the ILU runtime.
- `def LongReal(v):`

  Converts its `int`, `float`, or sixteen-integer `list` or `tuple` argument to type `ilu_LongReal`. In case of a list or tuple, the elements encode the bytes of the IEEE long real value, from most significant to least.

- `def LookupObject(sid, ih, cl):`

  Returns the object with object server ID sid, object instance handle ih, and Python class `cl`, assuming it was previously published using the simple binding service. If the lookup fails, `None` is returned.

- `def ObjectOfSBH(cl, sbh):`

  Returns the object corresponding to the Python class cl and string binding handle sbh.

- `def ParseSBH(sbh):`

  Returns the pair (object id, contact info) corresponding to the string binding handle sbh.

- `def IOROfObject(obj):`

  If the `IIOP` protocol has been configured in, returns the string IOR of the object, as specified in the CORBA 2 IIOP specification. If the `IIOP` protocol has not been configured in, throws an error.

- `def RegisterInputHandler (file, handler_fn):`

  Sets up an association between the file (which must be a file object opened for reading), and the handler_fn (which must be a callable function with no arguments) so that handler_fn is called whenever input is available on file. This is useful for implementing a server that also responds to commands typed to its standard input, for example. Passing a value of `None` for the handler_fn removes the association.

- `def RunMainLoop(loophandle):`

  Runs the ILU main loop. The argument is a "handle" on that loop invocation, created by a call to `ilu.CreateLoopHandle()`.

- `def SetMainLoop(DoEvent, RegisterInput, CancelInput, RegisterOutput, CancelOutput, CreateAlarm, SetAlarm, CancelAlarm)`

  The purpose of this function is to be able to use a foreign main loop (such as for a user interface toolkit) with an ILU server. The details will not be described here. Look at the runtime module ``ilu_tk`` for an example of its use.

- `def SetDebugLevel(bits):` or `def SetDebugLevel(string):`

  Sets the ILU kernel debugging flags according to its `int` argument, if an int is specified, or via the colon-separated list of debug switches, if a string is specified. See the Debugging section of the ILU Manual for more information on these switches.

- `def ThreadedOperation():`

  Enables thread use in both the ILU kernel and the ILU/Python runtime. This routine should be called before any other ILU calls are made.

- `TRUE = 1`
- `def TypeID(cl):`

  Returns the ILU unique type identifier corresponding to the Python class cl.

- `def TypeName(cl):`

  Returns the ILU type name corresponding to the Python class cl.

- `Version = ...` is the ILU version string.

Built-in methods of ILU objects:

- `IluObjectID()` returns the object ID of the object.
- `IluPublish()` publishes the object using the simple binding service.
- `IluSBH()` returns the object's string binding handle.
- `IluTypeID()` returns the unique type identifier of the object's ILU type.
- `IluTypeName()` returns the type name of the object's ILU type.
- `IluWithdraw()` undoes the effect of IluPublish().

Special attributes of ILU true objects: One or more of the following attributes may be set in a true (implementation) object of an ISL object type to control certain aspects of that object.

- `IluInstHandle`, a string instance variable, gives the object's instance handle. If not present, an instance handle is invented automatically.
- `IluServer`, a variable of type `ilu_Server`, determines the object's server. This can be an instance or a class variable. If not present, the default server is used.

## Stub Generation

To generate Python stubs from an ISL file, use the program `python-stubber`. Two files are generated from each ISL `INTERFACE name`:

- ``name.py'` containing code for constants, exceptions, and types defined in the interface, and
- ``name__skel.py'` containing code for the skeletons (server stubs) for object types defined in the interface.

Go to the previous, next section.