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