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