mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			361 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			361 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<HTML><HEAD><TITLE>Creating a C extension module on the Macintosh</TITLE></HEAD>
 | 
						|
<BODY>
 | 
						|
<H1>Creating a C extension module on the Macintosh</H1>
 | 
						|
<HR>
 | 
						|
 | 
						|
This document gives a step-by-step example of how to create a new C
 | 
						|
extension module on the mac. For this example, we will create a module
 | 
						|
to interface to the programmers' API of InterSLIP, a package that
 | 
						|
allows you to use MacTCP (and, hence, all internet services) over a
 | 
						|
modem connection. <p>
 | 
						|
 | 
						|
<H2>Prerequisites</H2>
 | 
						|
 | 
						|
There are a few things you need to pull this off. First and foremost,
 | 
						|
you need a C development environment. Actually, you need a specific
 | 
						|
development environment, CodeWarrior by <A
 | 
						|
HREF="http://www.metrowerks.com/">MetroWerks</A>.  You will probably
 | 
						|
need the latest version. You may be able to get by with an older
 | 
						|
version of CodeWarrior or with another development environment (Up to
 | 
						|
about 1994 python was developed with THINK C, and in the dim past it
 | 
						|
was compiled with MPW C) assuming you have managed to get Python to
 | 
						|
compile under your development environment, but the step-by-step
 | 
						|
character of this document will be lost. <p>
 | 
						|
 | 
						|
Next, you need a <A
 | 
						|
HREF="http://www.python.org/python/Sources.html">python source
 | 
						|
distribution</A>.  For PowerPC and cfm68k development you can actually
 | 
						|
get by without a full source distribution, using the Development
 | 
						|
distribution. You'll also need a functional python interpreter, and
 | 
						|
the Modulator program (which lives in <CODE>Tools:Modulator</CODE> in
 | 
						|
the standard source distribution). You may also find that Guido's <A
 | 
						|
HREF="http://www.python.org/doc/ext/ext.html">Extending and embedding
 | 
						|
the Python interpreter</A> is a very handy piece of documentation. I
 | 
						|
will skip lots of details that are handled there, like complete
 | 
						|
descriptions of <CODE>Py_ParseTuple</CODE> and such utility routines, or
 | 
						|
the general structure of extension modules. <p>
 | 
						|
 | 
						|
<H2>InterSLIP and the C API to it</H2>
 | 
						|
 | 
						|
InterSLIP, the utility to which we are going to create a python
 | 
						|
interface, is a system extension that does all the work of connecting
 | 
						|
to the internet over a modem connection. InterSLIP is provided
 | 
						|
free-of-charge by <A
 | 
						|
HREF="http://www.intercon.com/">InterCon</A>. First it connects to
 | 
						|
your modem, then it goes through the whole process of dialling,
 | 
						|
logging in and possibly starting the SLIP software on the remote
 | 
						|
computer and finally it starts with the real work: packing up IP
 | 
						|
packets handed to it by MacTCP and sending them to the remote side
 | 
						|
(and, of course, the reverse action of receiving incoming packets,
 | 
						|
unpacking them and handing them to MacTCP). InterSLIP is a device
 | 
						|
driver, and you control it using a application supplied with it,
 | 
						|
InterSLIP Setup. The API that InterSLIP Setup uses to talk to the
 | 
						|
device driver is published in the documentation and, hence, also
 | 
						|
useable by other applications. <p>
 | 
						|
 | 
						|
I happened to have a C interface to the API, which is all ugly
 | 
						|
low-level device-driver calls by itself. The C interface is in <A
 | 
						|
HREF="interslip/InterslipLib.c">InterslipLib.c</A> and <A
 | 
						|
HREF="interslip/InterslipLib.h">InterslipLib.h</A>, we'll
 | 
						|
concentrate here on how to build the Python wrapper module around
 | 
						|
it. Note that this is the "normal" situation when you are writing a
 | 
						|
Python extension module: you have some sort of functionality available
 | 
						|
to C programmers and want to make a Python interface to it. <p>
 | 
						|
 | 
						|
<H2>Using Modulator</H2>
 | 
						|
 | 
						|
The method we describe in this document, using Modulator, is the best
 | 
						|
method for small interfaces. For large interfaces there is another
 | 
						|
tool, Bgen, which actually generates the complete module without you
 | 
						|
lifting a single finger. Bgen, however, has the disadvantage of having
 | 
						|
a very steep learning curve, so an example using it will have to wait
 | 
						|
until another document, when I have more time. <p>
 | 
						|
 | 
						|
First, let us look at the <A
 | 
						|
HREF="interslip/InterslipLib.h">InterslipLib.h</A> header file,
 | 
						|
and see that the whole interface consists of six routines:
 | 
						|
<CODE>is_open</CODE>, <CODE>is_connect</CODE>,
 | 
						|
<CODE>is_disconnect</CODE>, <CODE>is_status</CODE>,
 | 
						|
<CODE>is_getconfig</CODE> and <CODE>is_setconfig</CODE>.  Our first
 | 
						|
step will be to create a skeleton file <A
 | 
						|
HREF="interslip/@interslipmodule.c">@interslipmodule.c</A>, a
 | 
						|
dummy module that will contain all the glue code that python expects
 | 
						|
of an extension module. Creating this glue code is a breeze with
 | 
						|
modulator, a tool that we only have to tell that we want to create a
 | 
						|
module with methods of the six names above and that will create the
 | 
						|
complete skeleton C code for us. <p>
 | 
						|
 | 
						|
Why call this dummy module <CODE>@interslipmodule.c</CODE> and not
 | 
						|
<CODE>interslipmodule.c</CODE>? Self-preservation: if ever you happen
 | 
						|
to repeat the whole process after you have actually turned the
 | 
						|
skeleton module into a real module you would overwrite your
 | 
						|
hand-written code. By calling the dummy module a different name you
 | 
						|
have to make <EM>two</EM> mistakes in a row before you do this. <p>
 | 
						|
 | 
						|
If you installed Tk support when you installed Python this is extremely
 | 
						|
simple. You start modulator and are provided with a form in which you
 | 
						|
fill out the details of the module you are creating. <p>
 | 
						|
 | 
						|
<IMG SRC="html.icons/modulator.gif" ALIGN=CENTER><p>
 | 
						|
 | 
						|
You'll need to supply a module name (<CODE>interslip</CODE>, in our
 | 
						|
case), a module abbreviation (<CODE>pyis</CODE>, which is used as a
 | 
						|
prefix to all the routines and data structures modulator will create
 | 
						|
for you) and you enter the names of all the methods your module will
 | 
						|
export (the list above, with <CODE>is_</CODE> stripped off). Note that
 | 
						|
we use <CODE>pyis</CODE> as the prefix instead of the more logical
 | 
						|
<CODE>is</CODE>, since the latter would cause our routine names to
 | 
						|
collide with those in the API we are interfacing to! The method names
 | 
						|
are the names as seen by the python program, and the C routine names
 | 
						|
will have the prefix and an underscore prepended. Modulator can do
 | 
						|
much more, like generating code for objects and such, but that is a
 | 
						|
topic for a later example. <p>
 | 
						|
 | 
						|
Once you have told modulator all about the module you want to create
 | 
						|
you press "check", which checks that you haven't omitted any
 | 
						|
information and "Generate code". This will prompt you for a C output
 | 
						|
file and generate your module for you. <p>
 | 
						|
 | 
						|
<H2>Using Modulator without Tk</H2>
 | 
						|
 | 
						|
 | 
						|
Modulator actually uses a two-stage process to create your code: first
 | 
						|
the information you provided is turned into a number of python
 | 
						|
statements and then these statements are executed to generate your
 | 
						|
code. This is done so that you can even use modulator if you don't
 | 
						|
have Tk support in Python: you'll just have to write the modulator
 | 
						|
python statements by hand (about 10 lines, in our example) and
 | 
						|
modulator will generate the C code (about 150 lines, in our
 | 
						|
example). Here is the Python code you'll want to execute to generate
 | 
						|
our skeleton module: <p>
 | 
						|
 | 
						|
<CODE><PRE>
 | 
						|
	import addpack
 | 
						|
	addpack.addpack('Tools')
 | 
						|
	addpack.addpack('modulator')
 | 
						|
	import genmodule
 | 
						|
 | 
						|
	m = genmodule.module()
 | 
						|
	m.name = 'interslip'
 | 
						|
	m.abbrev = 'pyis'
 | 
						|
	m.methodlist = ['open', 'connect', 'disconnect', 'status', \
 | 
						|
		'getconfig', 'setconfig']
 | 
						|
	m.objects = []
 | 
						|
 | 
						|
	fp = open('@interslipmodule.c', 'w')
 | 
						|
	genmodule.write(fp, m)
 | 
						|
</PRE></CODE>
 | 
						|
 | 
						|
Drop this program on the python interpreter and out will come your
 | 
						|
skeleton module. <p>
 | 
						|
 | 
						|
Now, rename the file to interslipmodule.c and you're all set to start
 | 
						|
developing. The module is complete in the sense that it should
 | 
						|
compile, and that if you import it in a python program you will see
 | 
						|
all the methods.  It is, of course, not yet complete in a functional
 | 
						|
way... <p>
 | 
						|
 | 
						|
<H2>Adding a module to Classic 68K Python</H2>
 | 
						|
 | 
						|
What you do now depends on whether you're developing for PowerPC (or
 | 
						|
for CFM68K) or for "traditional" mac. For a traditional 68K Python,
 | 
						|
you will have to add your new module to the project file of the Python
 | 
						|
interpreter, and you have to edit "config.c" to add the module to the
 | 
						|
set of builtin modules. In config.c you will add the module at two
 | 
						|
places: near the start of the file there is a list of external
 | 
						|
declarations for all init() routines. Add a line of the form
 | 
						|
<CODE><PRE>
 | 
						|
		extern void initinterslip();
 | 
						|
</PRE></CODE>
 | 
						|
here. Further down the file there is an array that is initialized with
 | 
						|
modulename/initfunction pairs. Add a line of the form
 | 
						|
<CODE><PRE>
 | 
						|
		{"interslip",	initinterslip},
 | 
						|
</PRE></CODE>
 | 
						|
here. You may want to bracket these two lines with
 | 
						|
<CODE><PRE>
 | 
						|
	#ifdef USE_INTERSLIP
 | 
						|
	#endif
 | 
						|
</PRE></CODE>
 | 
						|
lines, that way you can easily control whether the module is
 | 
						|
incorporated into python at compile time. If you decide to do the
 | 
						|
latter edit your config file (you can find the name in the "C/C++
 | 
						|
language" section of the MW preferences dialog, it will probably be
 | 
						|
"mwerks_nonshared_config.h") and add a
 | 
						|
<CODE><PRE>
 | 
						|
	#define USE_INTERSLIP
 | 
						|
</PRE></CODE>
 | 
						|
 | 
						|
Make the new interpreter and check that you can import the module, see
 | 
						|
the methods (with "dir(interslip)") and call them. <p>
 | 
						|
 | 
						|
<H2>Creating a plugin module</H2>
 | 
						|
 | 
						|
For PowerPC or cfm68k development you could follow the same path, but it is
 | 
						|
actually a better idea to use a dynamically loadable module. The
 | 
						|
advantage of dynamically loadable modules is that they are not loaded
 | 
						|
until a python program actually uses them (resulting in less memory
 | 
						|
usage by the interpreter) and that development is a lot simpler (since
 | 
						|
your projects will all be smaller). Moreover, you can distribute a
 | 
						|
plugin module by itself without haveing to distribute a complete
 | 
						|
python interpreter. <p>
 | 
						|
 | 
						|
Go to the "PlugIns" folder and copy the files xx.prj,
 | 
						|
and xx.prj.exp to interslipmodule.prj and
 | 
						|
interslipmodule.prj.exp, respectively. Edit
 | 
						|
interslipmodule.prj.exp and change the name of the exported routine
 | 
						|
"initxx" to "initinterslip".  Open interslipmodule.prj with CodeWarrior,
 | 
						|
remove the file xxmodule.c and add interslipmodule.c and make a number
 | 
						|
of adjustments to the preferences:
 | 
						|
<UL>
 | 
						|
<LI> in PPC target, set the output file name to "interslipmodule.pcc.slb",
 | 
						|
<LI> in cfm68k target set the output file name to "interslipmodule.cfm68k.slb".
 | 
						|
<LI> if you are working without a source distribution (i.e. with a normal
 | 
						|
binary distribution plus a development distribution) you will not have
 | 
						|
a file <code>PythonCore</code>. The installation process has deposited this
 | 
						|
file in the System <code>Extensions</code> folder under the name
 | 
						|
<code>PythonCore <i>version</i></code>. Add that file to the project, replacing
 | 
						|
<code>PythonCore</code>.
 | 
						|
</UL>
 | 
						|
Next, compile and link your module, fire up python and do the same
 | 
						|
tests as for 68K python. <p>
 | 
						|
 | 
						|
<H2>Getting the module to do real work</H2>
 | 
						|
 | 
						|
So far, so good. In half an hour or so we have created a complete new
 | 
						|
extension module for Python. The downside, however, is that the module
 | 
						|
does not do anything useful. So, in the next half hour we will turn
 | 
						|
our beautiful skeleton module into something that is at least as
 | 
						|
beautiful but also gets some serious work done.  For this once,
 | 
						|
<EM>I</EM> have spent that half hour for you, and you can see the
 | 
						|
results in <A
 | 
						|
HREF="interslip/interslipmodule.c">interslipmodule.c</A>. <p>
 | 
						|
 | 
						|
We add
 | 
						|
<CODE><PRE>
 | 
						|
	#include "InterslipLib.h"
 | 
						|
	#include "macglue.h"
 | 
						|
</PRE></CODE>
 | 
						|
to the top of the file, and work our way through each of the methods
 | 
						|
to add the functionality needed. Starting with open, we fill in the
 | 
						|
template docstring, the value accessible from Python by looking at
 | 
						|
<CODE>interslip.open.__doc__</CODE>.  There are not many tools using
 | 
						|
this information at the moment, but as soon as class browsers for
 | 
						|
python become available having this minimal documentation available is
 | 
						|
a good idea. We put "Load the interslip driver" as the comment
 | 
						|
here. <p>
 | 
						|
 | 
						|
Next, we tackle the body of <CODE>pyis_open()</CODE>.  Since it has no
 | 
						|
arguments and no return value we don't need to mess with that, we just
 | 
						|
have to add a call to <CODE>is_open()</CODE> and check the return for
 | 
						|
an error code, in which case we raise an error:
 | 
						|
<CODE><PRE>
 | 
						|
	err = is_open();
 | 
						|
	if ( err ) {
 | 
						|
		PyErr_Mac(ErrorObject, err);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
</PRE></CODE>
 | 
						|
The routine <CODE><A NAME="PyErr_Mac">PyErr_Mac()</A></CODE> is a
 | 
						|
useful routine that raises the exception passed as its first
 | 
						|
argument. The data passed with the exception is based on the standard
 | 
						|
MacOS error code given, and PyErr_Mac() attempts to locate a textual
 | 
						|
description of the error code (which sure beats the "error -14021"
 | 
						|
messages that so many macintosh applications tell their poor
 | 
						|
users). <p>
 | 
						|
 | 
						|
We will skip pyis_connect and pyis_disconnect here, which are pretty
 | 
						|
much identical to pyis_open: no arguments, no return value, just a
 | 
						|
call and an error check. With pyis_status() things get interesting
 | 
						|
again: this call still takes 3 arguments, and all happen to be values
 | 
						|
returned (a numeric connection status indicator, a message sequence
 | 
						|
number and a pointer to the message itself, in MacOS pascal-style
 | 
						|
string form). We declare variables to receive the returned values, do
 | 
						|
the call, check the error and format the return value. <p>
 | 
						|
 | 
						|
Building the return value is done using <CODE><A
 | 
						|
NAME="Py_BuildValue">Py_BuildValue</A></CODE>:
 | 
						|
<CODE><PRE>
 | 
						|
	return Py_BuildValue("iiO&", (int)status, (int)seqnum, PyMac_BuildStr255, message);
 | 
						|
</PRE></CODE>
 | 
						|
Py_BuildValue() is a very handy routine that builds tuples according
 | 
						|
to a format string, somewhat similar to the way <CODE>printf()</CODE>
 | 
						|
works.  The format string specifies the arguments expected after the
 | 
						|
string, and turns them from C objects into python objects. The
 | 
						|
resulting objects are put in a python tuple object and returned. The
 | 
						|
"i" format specifier signifies an "int" (hence the cast: status and
 | 
						|
seqnum are declared as "long", which is what the is_status() routine
 | 
						|
wants, and even though we use a 4-byte project there is really no
 | 
						|
reason not to put the cast here). Py_BuildValue and its counterpart
 | 
						|
Py_ParseTuple have format codes for all the common C types like ints,
 | 
						|
shorts, C-strings, floats, etc. Also, there is a nifty escape
 | 
						|
mechanism to format values about which is does not know. This is
 | 
						|
invoked by the "O&" format: it expects two arguments, a routine
 | 
						|
pointer and an int-sized data object. The routine is called with the
 | 
						|
object as a parameter and it should return a python objects
 | 
						|
representing the data. <CODE>Macglue.h</CODE> declares a number of
 | 
						|
such formatting routines for common MacOS objects like Str255, FSSpec,
 | 
						|
OSType, Rect, etc. See the comments in the include file for
 | 
						|
details. <p>
 | 
						|
 | 
						|
<CODE>Pyis_getconfig()</CODE> is again similar to pyis_getstatus, only
 | 
						|
two minor points are worth noting here.  First, the C API return the
 | 
						|
input and output baudrate squashed together into a single 4-byte
 | 
						|
long. We separate them out before returning the result to
 | 
						|
python. Second, whereas the status call returned us a pointer to a
 | 
						|
<CODE>Str255</CODE> it kept we are responsible for allocating the
 | 
						|
<CODE>Str255</CODE> for getconfig. This is something that would have
 | 
						|
been easy to get wrong had we not used prototypes everywhere. Morale:
 | 
						|
always try to include the header files for interfaces to libraries and
 | 
						|
other stuff, so that the compiler can catch any mistakes you make. <p>
 | 
						|
 | 
						|
<CODE>Pyis_setconfig()</CODE> finally shows off
 | 
						|
<CODE>Py_ParseTuple</CODE>, the companion function to
 | 
						|
<CODE>Py_BuildValue</CODE>.  You pass it the argument tuple "args"
 | 
						|
that your method gets as its second argument, a format string and
 | 
						|
pointers to where you want the arguments stored. Again, standard C
 | 
						|
types such as strings and integers Py_ParseTuple knows all about and
 | 
						|
through the "O&" format you can extend the functionality. For each
 | 
						|
"O&" you pass a function pointer and a pointer to a data area. The
 | 
						|
function will be called with a PyObject pointer and your data pointer
 | 
						|
and it should convert the python object to the correct C type. It
 | 
						|
should return 1 on success and 0 on failure. Again, a number of
 | 
						|
converters for standard MacOS types are provided, and declared in
 | 
						|
<CODE>macglue.h</CODE>. <p>
 | 
						|
 | 
						|
Next in our source file comes the method table for our module, which
 | 
						|
has been generated by modulator (and it did a good job too!), but
 | 
						|
which is worth looking at for a moment.  Entries are of the form
 | 
						|
<CODE><PRE>
 | 
						|
	{"open",	pyis_open,	1,	pyis_open__doc__},
 | 
						|
</PRE></CODE>
 | 
						|
where the entries are python method name, C routine pointer, flags and
 | 
						|
docstring pointer.  The value to note is the 1 for the flags: this
 | 
						|
signifies that you want to use "new-style" Py_ParseTuple behaviour. If
 | 
						|
you are writing a new module always use this, but if you are modifying
 | 
						|
old code which calls something like <CODE>getargs(args, "(ii)",
 | 
						|
...)</CODE> you will have to put zero here. See "extending and
 | 
						|
embedding" or possibly the getargs.c source file for details if you
 | 
						|
need them. <p>
 | 
						|
 | 
						|
Finally, we add some code to the init module, to put some symbolic
 | 
						|
constants (codes that can by returned by the status method) in the
 | 
						|
module dictionary, so the python program can use "interslip.RUN"
 | 
						|
instead of the cryptic "4" when it wants to check that the interslip
 | 
						|
driver is in RUN state. Modulator has already generated code to get at
 | 
						|
the module dictionary using PyModule_GetDict() to store the exception
 | 
						|
object, so we simply call
 | 
						|
<CODE><PRE>
 | 
						|
	PyDict_SetItemString(d, "IDLE", PyInt_FromLong(IS_IDLE));
 | 
						|
</PRE></CODE>
 | 
						|
for each of our items. Since the last bit of code in our init routine
 | 
						|
checks for previous errors with <CODE>PyErr_Occurred()</CODE> and
 | 
						|
since <CODE>PyDict_SetItemString()</CODE> gracefully handles the case
 | 
						|
of <CODE>NULL</CODE> parameters (if <CODE>PyInt_FromLong()</CODE>
 | 
						|
failed, for instance) we don't have to do error checking here. In some
 | 
						|
other cases you may have to do error checking yourself. <p>
 | 
						|
 | 
						|
This concludes our crash-course on writing Python extensions in C on
 | 
						|
the Macintosh.  If you are not done reading yet I suggest you look
 | 
						|
back at the <A HREF="index.html">MacPython Crashcourse index</A> to
 | 
						|
find another topic to study. <p>
 |