2.13 How to produce a Python module

This section describes how it is possible to produce a Python module from Modula-2 code. There are a number of advantages to this approach, it ensures your code reaches a wider audience, maybe it is easier to initialize your application in Python.

The example application here is a pedagogical two dimensional gravity next event simulation. The Python module needs to have a clear API which should be placed in a single definition module. Furthermore the API should only use fundamental pervasive data types and strings. Below the API is contained in the file twoDsim.def:

DEFINITION MODULE twoDsim ;

EXPORT UNQUALIFIED gravity, box, poly3, poly5, poly6, mass,
                   fix, circle, pivot, velocity, accel, fps,
                   replayRate, simulateFor ;
(*
   gravity - turn on gravity at: g m^2
*)

PROCEDURE gravity (g: REAL) ;


(*
   box - place a box in the world at (x0,y0),(x0+i,y0+j)
*)

PROCEDURE box (x0, y0, i, j: REAL) : CARDINAL ;


(*
   poly3 - place a triangle in the world at:
           (x0,y0),(x1,y1),(x2,y2)
*)

PROCEDURE poly3 (x0, y0, x1, y1, x2, y2: REAL) : CARDINAL ;


(*
   poly5 - place a pentagon in the world at:
           (x0,y0),(x1,y1),(x2,y2),(x3,y3),(x4,y4)
*)

PROCEDURE poly5 (x0, y0, x1, y1,
                 x2, y2, x3, y3, x4, y4: REAL) : CARDINAL ;


(*
   poly6 - place a hexagon in the world at:
           (x0,y0),(x1,y1),(x2,y2),(x3,y3),(x4,y4),(x5,y5)
*)

PROCEDURE poly6 (x0, y0, x1, y1,
                 x2, y2, x3, y3,
                 x4, y4, x5, y5: REAL) : CARDINAL ;


(*
   mass - specify the mass of an object and return the, id.
*)

PROCEDURE mass (id: CARDINAL; m: REAL) : CARDINAL ;


(*
   fix - fix the object to the world.
*)

PROCEDURE fix (id: CARDINAL) : CARDINAL ;


(*
   circle - adds a circle to the world.  Center
            defined by: x0, y0 radius, r.
*)

PROCEDURE circle (x0, y0, r: REAL) : CARDINAL ;


(*
   velocity - give an object, id, a velocity, vx, vy.
*)

PROCEDURE velocity (id: CARDINAL; vx, vy: REAL) : CARDINAL ;


(*
   accel - give an object, id, an acceleration, ax, ay.
*)

PROCEDURE accel (id: CARDINAL; ax, ay: REAL) : CARDINAL ;


(*
   fps - set frames per second.
*)

PROCEDURE fps (f: REAL) ;


(*
   replayRate - set frames per second during replay.
*)

PROCEDURE replayRate (f: REAL) ;


(*
   simulateFor - render for, t, seconds.
*)

PROCEDURE simulateFor (t: REAL) ;


END twoDsim.

The keyword UNQUALIFIED can be used to ensure that the compiler will provide externally accessible functions gravity, box, poly3, poly5, poly6, mass, fix, circle, pivot, velocity, accel, fps, replayRate, simulateFor rather than name mangled alternatives. Hence in our Python3 application we could write:

#!/usr/bin/env python3

from twoDsim import *

b = box (0.0, 0.0, 1.0, 1.0)
b = fix (b)
c1 = circle (0.7, 0.7, 0.05)
c1 = mass (c1, 0.01)
c2 = circle (0.7, 0.1, 0.05)
c2 = mass (c2, 0.01)
c2 = fix (c2)
gravity (-9.81)
fps (24.0*4.0)
replayRate (24.0)
print ("creating frames")
try:
    simulateFor (1.0)
    print ("all done")
except:
    print ("exception raised")

which accesses the various functions defined and implemented by the module twoDsim. The Modula-2 source code is compiled via:

$ gm2 -g -fiso -c -fswig twoDsim.mod
$ gm2 -g -fiso -c -fmakelist twoDsim.mod
$ gm2 -g -fiso -c -fmakeinit twoDsim.mod

The first command both compiles the source file creating twoDsim.o and produces a swig interface file swig.i. We now use swig and g++ to produce and compile the interface wrappers:

$ libtool --mode=compile g++ -g -c twoDsim_m2.cpp -o twoDsim_m2.lo
$ swig -c++ -python3 twoDsim.i
$ libtool --mode=compile g++ -c -fPIC twoDsim_wrap.cxx \
   -I/usr/include/python3 -o twoDsim_wrap.lo
$ libtool --mode=compile gm2 -g -fPIC -fiso -c deviceGnuPic.mod
$ libtool --mode=compile gm2 -g -fPIC -fiso -c roots.mod
$ libtool --mode=compile gm2 -g -fPIC -fiso -c -fswig \
   twoDsim.mod -o twoDsim.lo

Finally the application is linked into a shared library:

$ libtool --mode=link gcc -g twoDsim_m2.lo twoDsim_wrap.lo \
  roots.lo deviceGnuPic.lo \
   -L${prefix}/lib64 \
   -rpath `pwd` -lgm2 -lstdc++ -lm -o libtwoDsim.la
cp .libs/libtwoDsim.so _twoDsim.so

The library name must start with _ to comply with the Python3 module naming scheme.