blob: 465603c7be47a3750df1bc02553e8fe791e9eabe [file] [log] [blame]
Jack Jansena6308131996-03-18 13:38:52 +00001<HTML><HEAD><TITLE>Creating a C extension module on the Macintosh</TITLE></HEAD>
2<BODY>
3<H1>Creating a C extension module on the Macintosh</H1>
4<HR>
5
6This document gives a step-by-step example of how to create a new C
7extension module on the mac. For this example, we will create a module
8to interface to the programmers' API of InterSLIP, a package that
9allows you to use MacTCP (and, hence, all internet services) over a
10modem connection. <p>
11
12<H2>Prerequisites</H2>
13
14There are a few things you need to pull this off. First and foremost,
15you need a C development environment. Actually, you need a specific
16development environment, CodeWarrior by <A
17HREF="http://www.metrowerks.com/">MetroWerks</A>. You will probably
18need the latest version. You may be able to get by with an older
19version of CodeWarrior or with another development environment (Up to
20about 1994 python was developed with THINK C, and in the dim past it
21was compiled with MPW C) assuming you have managed to get Python to
22compile under your development environment, but the step-by-step
23character of this document will be lost. <p>
24
25Next, you need a <A HREF="http://www.python.org/python/Sources.html">python
26source distribution</A>. There is a <A
27HREF="update-to-1.3/into-PlugIns.hqx"> fixed project template</A> that
28you also need if you are going to make a dynamically loaded
29module. For PowerPC development you can actually get by without a full
30source distribution, using the PPC Development distribution (if I have
31gotten around to putting it together by the time you read
32this). You'll also need a functional python interpreter, and the
33Modulator program (which lives in <CODE>Tools:Modulator</CODE> in the
34standard source distribution). You may also find that Guido's <A
35HREF="http://www.python.org/doc/ext/ext.html">Extending and embedding
36the Python interpreter</A> is a very handy piece of documentation. I
37will skip lots of details that are handled there, like complete
38descriptions of <CODE>Py_ParseTuple</CODE> and such utility routines,
39or the general structure of extension modules. <p>
40
41<H2>InterSLIP and the C API to it</H2>
42
43InterSLIP, the utility to which we are going to create a python
44interface, is a system extension that does all the work of connecting
45to the internet over a modem connection. InterSLIP is provided
46free-of-charge by <A
47HREF="http://www.intercon.com/">InterCon</A>. First it connects to
48your modem, then it goes through the whole process of dialling,
49logging in and possibly starting the SLIP software on the remote
50computer and finally it starts with the real work: packing up IP
51packets handed to it by MacTCP and sending them to the remote side
52(and, of course, the reverse action of receiving incoming packets,
53unpacking them and handing them to MacTCP). InterSLIP is a device
54driver, and you control it using a application supplied with it,
55InterSLIP Setup. The API that InterSLIP Setup uses to talk to the
56device driver is published in the documentation and, hence, also
57useable by other applications. <p>
58
59I happened to have a C interface to the API, which is all ugly
60low-level device-driver calls by itself. The C interface is in <A
61HREF="interslip/InterslipLib.c">InterslipLib.c</A> and <A
62HREF="interslip/InterslipLib.h">InterslipLib.h</A>, we'll
63concentrate here on how to build the Python wrapper module around
64it. Note that this is the "normal" situation when you are writing a
65Python extension module: you have some sort of functionality available
66to C programmers and want to make a Python interface to it. <p>
67
68<H2>Using Modulator</H2>
69
70The method we describe in this document, using Modulator, is the best
71method for small interfaces. For large interfaces there is another
72tool, Bgen, which actually generates the complete module without you
73lifting a single finger. Bgen, however, has the disadvantage of having
74a very steep learning curve, so an example using it will have to wait
75until another document, when I have more time. <p>
76
77First, let us look at the <A
78HREF="interslip/InterslipLib.h">InterslipLib.h</A> header file,
79and see that the whole interface consists of six routines:
80<CODE>is_open</CODE>, <CODE>is_connect</CODE>,
81<CODE>is_disconnect</CODE>, <CODE>is_status</CODE>,
82<CODE>is_getconfig</CODE> and <CODE>is_setconfig</CODE>. Our first
83step will be to create a skeleton file <A
84HREF="interslip/@interslipmodule.c">@interslipmodule.c</A>, a
85dummy module that will contain all the glue code that python expects
86of an extension module. Creating this glue code is a breeze with
87modulator, a tool that we only have to tell that we want to create a
88module with methods of the six names above and that will create the
89complete skeleton C code for us. <p>
90
91Why call this dummy module <CODE>@interslipmodule.c</CODE> and not
92<CODE>interslipmodule.c</CODE>? Self-preservation: if ever you happen
93to repeat the whole process after you have actually turned the
94skeleton module into a real module you would overwrite your
95hand-written code. By calling the dummy module a different name you
96have to make <EM>two</EM> mistakes in a row before you do this. <p>
97
98On systems with the Tk windowing API for Python (currently only
99unix/X11 systems, but mac support may be available when you read this)
100this is extremely simple. It is actually so simple that it pays to
101create the skeleton module under unix and ship the code to your
102mac. You start modulator and are provided with a form in which you
103fill out the details of the module you are creating. <p>
104
105<IMG SRC="html.icons/modulator.gif" ALIGN=CENTER><p>
106
107You'll need to supply a module name (<CODE>interslip</CODE>, in our
108case), a module abbreviation (<CODE>pyis</CODE>, which is used as a
109prefix to all the routines and data structures modulator will create
110for you) and you enter the names of all the methods your module will
111export (the list above, with <CODE>is_</CODE> stripped off). Note that
112we use <CODE>pyis</CODE> as the prefix instead of the more logical
113<CODE>is</CODE>, since the latter would cause our routine names to
114collide with those in the API we are interfacing to! The method names
115are the names as seen by the python program, and the C routine names
116will have the prefix and an underscore prepended. Modulator can do
117much more, like generating code for objects and such, but that is a
118topic for a later example. <p>
119
120Once you have told modulator all about the module you want to create
121you press "check", which checks that you haven't omitted any
122information and "Generate code". This will prompt you for a C output
123file and generate your module for you. <p>
124
125<H2>Using Modulator without Tk</H2>
126
127
128Modulator actually uses a two-stage process to create your code: first
129the information you provided is turned into a number of python
130statements and then these statements are executed to generate your
131code. This is done so that you can even use modulator if you don't
132have Tk support in Python: you'll just have to write the modulator
133python statements by hand (about 10 lines, in our example) and
134modulator will generate the C code (about 150 lines, in our
135example). Here is the Python code you'll want to execute to generate
136our skeleton module: <p>
137
138<CODE><PRE>
139 import addpack
140 addpack.addpack('Tools')
141 addpack.addpack('modulator')
142 import genmodule
143
144 m = genmodule.module()
145 m.name = 'interslip'
146 m.abbrev = 'pyis'
147 m.methodlist = ['open', 'connect', 'disconnect', 'status', \
148 'getconfig', 'setconfig']
149 m.objects = []
150
151 fp = open('@interslipmodule.c', 'w')
152 genmodule.write(fp, m)
153</PRE></CODE>
154
155Drop this program on the python interpreter and out will come your
156skeleton module. <p>
157
158Now, rename the file to interslipmodule.c and you're all set to start
159developing. The module is complete in the sense that it should
160compile, and that if you import it in a python program you will see
161all the methods. It is, of course, not yet complete in a functional
162way... <p>
163
164<H2>Adding a module to 68K Python</H2>
165
166What you do now depends on whether you're developing for PowerPC (or
167for CFM68K) or for "traditional" mac. For a traditional 68K Python,
168you will have to add your new module to the project file of the Python
169interpreter, and you have to edit "config.c" to add the module to the
170set of builtin modules. In config.c you will add the module at two
171places: near the start of the file there is a list of external
172declarations for all init() routines. Add a line of the form
173<CODE><PRE>
174 extern void initinterslip();
175</PRE></CODE>
176here. Further down the file there is an array that is initialized with
177modulename/initfunction pairs. Add a line of the form
178<CODE><PRE>
179 {"interslip", initinterslip},
180</PRE></CODE>
181here. You may want to bracket these two lines with
182<CODE><PRE>
183 #ifdef USE_INTERSLIP
184 #endif
185</PRE></CODE>
186lines, that way you can easily control whether the module is
187incorporated into python at compile time. If you decide to do the
188latter edit your config file (you can find the name in the "C/C++
189language" section of the MW preferences dialog, it will probably be
190"mwerks_nonshared_config.h") and add a
191<CODE><PRE>
192 #define USE_INTERSLIP
193</PRE></CODE>
194
195Make the new interpreter and check that you can import the module, see
196the methods (with "dir(interslip)") and call them. <p>
197
198<H2>Creating a PowerPC plugin module</H2>
199
200For PowerPC development you could follow the same path, but it is
201actually a better idea to use a dynamically loadable module. The
202advantage of dynamically loadable modules is that they are not loaded
203until a python program actually uses them (resulting in less memory
204usage by the interpreter) and that development is a lot simpler (since
205your projects will all be smaller). Moreover, you can distribute a
206plugin module by itself without haveing to distribute a complete
207python interpreter. <p>
208
209Go to the "PlugIns" folder and copy the files xxmodule.µ,
210xxmodule_config.h and xxmodule.µ.exp to interslipmodule.µ,
211interslipmodule_config.h and interslipmodule.µ.exp, respectively. Edit
212interslipmodule.µ.exp and change the name of the exported routine
213"initxx" to "initinterslip". Open interslipmodule.µ with CodeWarrior,
214remove the file xxmodule.c and add interslipmodule.c and make a number
215of adjustments to the preferences:
216<UL>
217<LI> in C/C++ language, set the header file to interslipmodule_config.h
218<LI> in PPC linker, set the entry point to "initinterslip"
219<LI> in PPC PEF, set the fragment name to "interslipmodule"
220<LI> in PPC Project, set the output file name to "interslipmodule.slb".
221</UL>
222Next, compile and link your module, fire up python and do the same
223tests as for 68K python. <p>
224
225<H2>Getting the module to do real work</H2>
226
227So far, so good. In half an hour or so we have created a complete new
228extension module for Python. The downside, however, is that the module
229does not do anything useful. So, in the next half hour we will turn
230our beautiful skeleton module into something that is at least as
231beautiful but also gets some serious work done. For this once,
232<EM>I</EM> have spent that half hour for you, and you can see the
233results in <A
234HREF="interslip/interslipmodule.c">interslipmodule.c</A>. <p>
235
236We add
237<CODE><PRE>
238 #include "InterslipLib.h"
239 #include "macglue.h"
240</PRE></CODE>
241to the top of the file, and work our way through each of the methods
242to add the functionality needed. Starting with open, we fill in the
243template docstring, the value accessible from Python by looking at
244<CODE>interslip.open.__doc__</CODE>. There are not many tools using
245this information at the moment, but as soon as class browsers for
246python become available having this minimal documentation available is
247a good idea. We put "Load the interslip driver" as the comment
248here. <p>
249
250Next, we tackle the body of <CODE>pyis_open()</CODE>. Since it has no
251arguments and no return value we don't need to mess with that, we just
252have to add a call to <CODE>is_open()</CODE> and check the return for
253an error code, in which case we raise an error:
254<CODE><PRE>
255 err = is_open();
256 if ( err ) {
257 PyErr_Mac(ErrorObject, err);
258 return NULL;
259 }
260</PRE></CODE>
261The routine <CODE><A NAME="PyErr_Mac">PyErr_Mac()</A></CODE> is a
262useful routine that raises the exception passed as its first
263argument. The data passed with the exception is based on the standard
264MacOS error code given, and PyErr_Mac() attempts to locate a textual
265description of the error code (which sure beats the "error -14021"
266messages that so many macintosh applications tell their poor
267users). <p>
268
269We will skip pyis_connect and pyis_disconnect here, which are pretty
270much identical to pyis_open: no arguments, no return value, just a
271call and an error check. With pyis_status() things get interesting
272again: this call still takes 3 arguments, and all happen to be values
273returned (a numeric connection status indicator, a message sequence
274number and a pointer to the message itself, in MacOS pascal-style
275string form). We declare variables to receive the returned values, do
276the call, check the error and format the return value. <p>
277
278Building the return value is done using <CODE><A
279NAME="Py_BuildValue">Py_BuildValue</A></CODE>:
280<CODE><PRE>
281 return Py_BuildValue("iiO&", (int)status, (int)seqnum, PyMac_BuildStr255, message);
282</PRE></CODE>
283Py_BuildValue() is a very handy routine that builds tuples according
284to a format string, somewhat similar to the way <CODE>printf()</CODE>
285works. The format string specifies the arguments expected after the
286string, and turns them from C objects into python objects. The
287resulting objects are put in a python tuple object and returned. The
288"i" format specifier signifies an "int" (hence the cast: status and
289seqnum are declared as "long", which is what the is_status() routine
290wants, and even though we use a 4-byte project there is really no
291reason not to put the cast here). Py_BuildValue and its counterpart
292Py_ParseTuple have format codes for all the common C types like ints,
293shorts, C-strings, floats, etc. Also, there is a nifty escape
294mechanism to format values about which is does not know. This is
295invoked by the "O&" format: it expects two arguments, a routine
296pointer and an int-sized data object. The routine is called with the
297object as a parameter and it should return a python objects
298representing the data. <CODE>Macglue.h</CODE> declares a number of
299such formatting routines for common MacOS objects like Str255, FSSpec,
300OSType, Rect, etc. See the comments in the include file for
301details. <p>
302
303<CODE>Pyis_getconfig()</CODE> is again similar to pyis_getstatus, only
304two minor points are worth noting here. First, the C API return the
305input and output baudrate squashed together into a single 4-byte
306long. We separate them out before returning the result to
307python. Second, whereas the status call returned us a pointer to a
308<CODE>Str255</CODE> it kept we are responsible for allocating the
309<CODE>Str255</CODE> for getconfig. This is something that would have
310been easy to get wrong had we not used prototypes everywhere. Morale:
311always try to include the header files for interfaces to libraries and
312other stuff, so that the compiler can catch any mistakes you make. <p>
313
314<CODE>Pyis_setconfig()</CODE> finally shows off
315<CODE>Py_ParseTuple</CODE>, the companion function to
316<CODE>Py_BuildValue</CODE>. You pass it the argument tuple "args"
317that your method gets as its second argument, a format string and
318pointers to where you want the arguments stored. Again, standard C
319types such as strings and integers Py_ParseTuple knows all about and
320through the "O&" format you can extend the functionality. For each
321"O&" you pass a function pointer and a pointer to a data area. The
322function will be called with a PyObject pointer and your data pointer
323and it should convert the python object to the correct C type. It
324should return 1 on success and 0 on failure. Again, a number of
325converters for standard MacOS types are provided, and declared in
326<CODE>macglue.h</CODE>. <p>
327
328Next in our source file comes the method table for our module, which
329has been generated by modulator (and it did a good job too!), but
330which is worth looking at for a moment. Entries are of the form
331<CODE><PRE>
332 {"open", pyis_open, 1, pyis_open__doc__},
333</PRE></CODE>
334where the entries are python method name, C routine pointer, flags and
335docstring pointer. The value to note is the 1 for the flags: this
336signifies that you want to use "new-style" Py_ParseTuple behaviour. If
337you are writing a new module always use this, but if you are modifying
338old code which calls something like <CODE>getargs(args, "(ii)",
339...)</CODE> you will have to put zero here. See "extending and
340embedding" or possibly the getargs.c source file for details if you
341need them. <p>
342
343Finally, we add some code to the init module, to put some symbolic
344constants (codes that can by returned by the status method) in the
345module dictionary, so the python program can use "interslip.RUN"
346instead of the cryptic "4" when it wants to check that the interslip
347driver is in RUN state. Modulator has already generated code to get at
348the module dictionary using PyModule_GetDict() to store the exception
349object, so we simply call
350<CODE><PRE>
351 PyDict_SetItemString(d, "IDLE", PyInt_FromLong(IS_IDLE));
352</PRE></CODE>
353for each of our items. Since the last bit of code in our init routine
354checks for previous errors with <CODE>PyErr_Occurred()</CODE> and
355since <CODE>PyDict_SetItemString()</CODE> gracefully handles the case
356of <CODE>NULL</CODE> parameters (if <CODE>PyInt_FromLong()</CODE>
357failed, for instance) we don't have to do error checking here. In some
358other cases you may have to do error checking yourself. <p>
359
360This concludes our crash-course on writing Python extensions in C on
361the Macintosh. If you are not done reading yet I suggest you look
362back at the <A HREF="index.html">MacPython Crashcourse index</A> to
363find another topic to study. <p>