From a6308130bb9e130ec532e05a2784e170584a5ac7 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Mon, 18 Mar 1996 13:38:52 +0000 Subject: Explanations of the examples here --- Mac/Demo/applescript.html | 171 ++++++++++++++++++++++ Mac/Demo/example1.html | 211 +++++++++++++++++++++++++++ Mac/Demo/example2.html | 180 +++++++++++++++++++++++ Mac/Demo/index.html | 94 ++++++++++++ Mac/Demo/plugins.html | 363 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1019 insertions(+) create mode 100644 Mac/Demo/applescript.html create mode 100644 Mac/Demo/example1.html create mode 100644 Mac/Demo/example2.html create mode 100644 Mac/Demo/index.html create mode 100644 Mac/Demo/plugins.html diff --git a/Mac/Demo/applescript.html b/Mac/Demo/applescript.html new file mode 100644 index 0000000..35cbf49 --- /dev/null +++ b/Mac/Demo/applescript.html @@ -0,0 +1,171 @@ +Using Open Scripting Extension from Python + +

Using Open Scripting Extension from Python

+
+ +OSA support in Python is still far from complete, and what +support there is is likely to change in the forseeable future. Still, +there is already enough in place to allow you to do some nifty things +to other programs from your python program.

+ + +Actually, when we say "AppleScript" in this document we actually mean +"the Open Scripting Architecture", there is nothing +AppleScript-specific in the Python implementation.

+ + +In this example, we will look at a scriptable application, extract its +"AppleScript Dictionary" and generate a Python interface module from +that and use that module to control the application. Because we want +to concentrate on the OSA details we don't bother with a real +user-interface for our application.

+ +The application we are going to script is Eudora Light, a free mail +program from QualComm. This is a +very versatile mail-reader, and QualComm has an accompanying +commercial version once your needs outgrow Eudora Light. Our program +will tell Eudora to send queued mail, retrieve mail or quit.

+ +

Creating the Python interface module

+ +There is a tool in the standard distribution that looks through a file +for an 'AETE' or 'AEUT' resource, the internal representation of the +AppleScript dictionary. This tool is called +gensuitemodule.py, and lives in +Tools:bgen:ae. When we start it, it asks us for an input +file and we point it to the Eudora Light executable. It starts parsing +the AETE resource, and for each AppleEvent suite it finds it prompts +us for the filename of the resulting python module. Remember to change +folders for the first module, you don't want to clutter up the Eudora +folder with your python interfaces. If you want to skip a suite you +press cancel and the process continues with the next suite. In the +case of Eudora, you do not want to generate the Required +suite, because it will be empty. AppleScript understands that an empty +suite means "incorporate the whole standard suite by this name", +gensuitemodule does not currently understand this. Creating the empty +Required_Suite.py would hide the correct module of that +name from our application.

+ + +Time for a sidebar. If you want to re-create +Required_Suite.py or one of the other standard modules +you should look in System Folder:Extensions:Scripting +Additions:Dialects:English Dialect, that is where the core +AppleEvent dictionaries live. Also, if you are looking for the +Finder_Suite interface: don't look in the finder (it has +an old System 7.0 scripting suite), look at the extension Finder +Scripting Extension.

+ + +Let's glance at the Eudora_Suite.py just created. You +may want to open Script Editor alongside, and have a look at how it +interprets the dictionary. EudoraSuite.py starts with some +boilerplate, then come some dictionaries implementing the OSA +Enumerations, then a big class definition with methods for each +AppleScript Verb and finally some comments. The Enumerations we will +skip, it suffices to know that whenever you have to pass an enumerator +to a method you can pass the english name and don't have to bother +with the 4-letter type code. So, you can say +

+	eudora.notice(occurrence="mail_arrives")
+
+instead of the rather more cryptic +
+	eudora.notice(occurrence="wArv")
+
+ +The Eudora_Suite class is the bulk of the code +generated. For each verb it contains a method. Each method knows what +arguments the verb expects, and it makes handy use of the keyword +argument scheme introduced in Python 1.3 to present a palatable +interface to the python programmer. You will see that each method +calls some routines from aetools, an auxiliary module +living in Tools:bgen:ae which contains some other nifty +AppleEvent tools as well. Have a look at it sometime, there is (of +course) no documentation yet.

+ +The other thing you notice is that each method calls +self.send, but no such method is defined. You will have +to provide it by subclassing or multiple inheritance, as we shall see +later.

+ +The module ends with some comments. Sadly, gensuitemodule is not yet +able to turn the Object Specifiers into reasonable Python code. For +now, if you need object specifiers, you will have to use the routines +defined in aetools.py (and aetypes.py, which +it incorporates). You use these in the form aetools.Word(10, +aetools.Document(1)) where the corresponding AppleScript +terminology would be word 10 of the first +document. Examine the two modules mentioned above along with +the comments at the end of your suite module if you need to create +more than the standard object specifiers.

+ +

Using a Python suite module

+ +Now that we have created the suite module we can use it in an +application. We do this by creating a class that inherits +Eudora_Suite and the TalkTo class from +aetools. The TalkTo class is basically a +container for the send method used by the methods from +the suite classes.

+ +Actually, our class will also inherit Required_Suite, +because we also need functionality from that suite: the quit +command. Gensuitemodule could have created this completely derived +class for us, since it has access to all information needed to build +the class but unfortunately it does not do so at the moment. All in +all, the heart of our program looks like this: +

+	import Eudora_Suite, Required_Suite, aetools
+	
+	class Eudora(aetools.TalkTo, Required_Suite.Required_Suite, \
+				Eudora_Suite.Eudora_Suite):
+		pass
+
+ +Yes, our class body is pass, all functionality is already +provided by the base classes, the only thing we have to do is glue it +together in the right way.

+ +Looking at the sourcefile testeudora.py we see that it starts +with some imports (and some addpack calls to extend +sys.path to include Tools:bgen:ae, use of +ni should be preferred over addpack but I +have not managed to master it yet). Then we get the class definition +for our main object and a constant giving the signature of Eudora.

+ +This, again, needs a little explanation. There are various ways to +describe to AppleScript which program we want to talk to, but the +easiest one to use (from Python, at least) is creator +signature. Application name would be much nicer, but Python currently +does not have a module that interfaces to the Finder database (which +would allow us to map names to signatures). The other alternative, +ChooseApplication from the program-to-program toolbox, is +also not available from Python at the moment.

+ +The main program itself is a wonder of simplicity. We create the +object that talks to Eudora (passing the signature as argument), ask +the user what she wants and call the appropriate method of the talker +object. The use of keyword arguments with the same names as used by +AppleScript make passing the parameters a breeze.

+ +The exception handling does need a few comments, though. Since +AppleScript is basically a connectionless RPC protocol nothing happens +when we create to talker object. Hence, if the destination application +is not running we will not notice until we send our first +command. There is another thing to note about errors returned by +AppleScript calls: even though MacOS.Error is raised not +all of the errors are actually OSErr-type errors, some +are error codes returned by the server application. In that case, the +error message will be incorrect.

+ +That concludes our simple example. Again, let me emphasize that +scripting support in Python is not very complete at the moment, and +the details of how to use AppleEvents will definitely change in the +near future. This will not only fix all the ideosyncracies noted in +this document but also break existing programs, since the current +suite organization will have to change to fix some of the problems. +Still, if you want to experiment with AppleEvents right now: go ahead! +

diff --git a/Mac/Demo/example1.html b/Mac/Demo/example1.html new file mode 100644 index 0000000..ddb897c --- /dev/null +++ b/Mac/Demo/example1.html @@ -0,0 +1,211 @@ +Using python to create Macintosh applications, part one + +

Using python to create Macintosh applications, part one

+
+ +This document will show you how to create a simple mac-style +application using Python. We will glance at how to use dialogs and +resources.

+ +The example application we look at will be a simple program with a +dialog that allows you to control and monitor InterSLIP, a device +driver that connects your mac to the Internet via a modem connection. +Source and resource file +(in binary and BinHex form for +downloading) for this application are available in the example1 folder (which you will have to download +if you are reading this document over the net and if you want to look +at the resources).

+ +We will use a C extension module module "interslip" that allows a +Python program to control and monitor the behaviour of the low-level +driver, and we will create the user interface around that. If you want +to actually run the code, you will obvously need InterSLIP and the +interslip module. The latter is available as a dynamically loadable +extension for PowerPC macs, and may be compiled in your Python +interpreter for 68K macs. As of this writing there is still a slight +problem with the Python interslip module causing it to say "file not +found" if the driver is not loaded yet. The workaround is to load the +driver by starting InterSLIP Control and quitting it.

+ + +If you are interested in building your own extensions to python you +should check out the companion document Creating Macintosh Python C extensions, +which tells you how to build your own C extension. Not completely +coincidental this document uses the interslip module that we will use +here as an example.

+ + +

Creating dialog resources

+ +Let us start with the creative bit: building the dialogs and creating +an icon for our program. For this you need ResEdit, and a reasonable +working knowledge of how to use it. "Inside Mac" or various books on +macintosh programming will help here.

+ +There is one fine point that deserves to be mentioned here: resource numbering. Because often your +resources will be combined with those that the Python interpreter and +various standard modules need you should give your DLOG and DITL +resources numbers above 512. 128 and below are reserved for Apple, +128-255 for the Python interpreter and 256-511 for standard +modules. If you are writing a module that you will be distributing for +inclusion in other people's programs you may want to register a number +in the 256-511 range, contact Guido or myself or whoever you think is +"in charge" of Python for the Macintosh at the moment. Even though the +application we are writing at the moment will keep its resources in a +separate resource file it is still a good idea to make sure that no +conflicts arise: once you have opened your resource file any attempt +by the interpreter to open a dialog will also search your resource +file.

+ +Okay, let's have a look at InterslipControl-1.rsrc, our resource file. +The DLOG and accompanying DITL resource both have number 512. Since +ResEdit creates both with default ID=128 you should take care to +change the number on both. The dialog itself is pretty basic: four +buttons (connect, disconnect, update status and quit), two labels and +two status fields.

+ +

An application with a modal dialog

+ +Next, we will have to write the actual application. For this example, +we will use a modal dialog. This means that we will put up the dialog +and go into a loop asking the dialog manager for events (buttons +pushed). We handle the actions requested by the user until the quit +button is pressed, upon which we exit our loop (and the program). This +way of structuring your program is actually rather antisocial, since +you force the user to do whatever you, the application writer, happen +to want. A modal dialog leaves no way of escape whatsoever (except +command-option-escape), and is usually not a good way to structure +anything but the most simple questions. Even then: how often have you +been confronted with a dialog asking a question that you could not +answer because the data you needed was obscured by the dialog itself? +In the next example we will look at an application that does pretty +much the same as this one but in a more user-friendly way.

+ +On to the code itself, in file InterslipControl-1.py. Have +a copy handy before you read on. The file starts off with a +textstring giving a short description. Not many tools do anything with +this as yet, but at some point in the future we will have all +sorts of nifty class browser that will display this string, so just +include it. Just put a short description at the start of each module, +class, method and function. After the initial description and some +comments, we import the modules we need.

+ +EasyDialogs is a handy standard +module that provides you with routines that put up common text-only +modal dialogs: +

+ +Res is a pretty complete interface to +the MacOS Resource Manager, described fully in Inside Mac. There is +currently no documentation of it, but the Apple documentation (or +Think Ref) will help you on your way if you remember two points: + + +Similarly, Dlg is an interface to the +Dialog manager (with Dialogs being implemented as python objects and +routines with Dialog arguments being methods). The sys module you +know, I hope. Interslip, +finally, is the module with the interface to the InterSLIP driver. We +use four calls from it: + + +Next in the source file we get definitions for our dialog resource +number and for the item numbers in our dialog. These should match the +situation in our resource file InterslipControl-1.rsrc, +obviously. Then we get an array converting numeric state codes +returned by interslip.status() to textual messages.

+ +On to the main program. We start off with opening our resource file, +which should live in the same folder as the python source. If we +cannot open it we use EasyDialogs to print a message and +exit. You can try it: just move the resource file somewhere else for a +moment. Then, we try to open the interslip driver, again catching an +error. All modules that raise MacOS error +exceptions will pass a 2-tuple to the exception handler with the +first item being the numeric OSErr code and the second +one being an informative message. If no informative message is +available it will be the rather uninformative "MacOS Error +-12345", but at least the second item will always be a +printable string. Finally we call do_dialog() to do the real work.

+ +Do_dialog() uses Dlg.GetNewDialog() to open +a dialog window initialized from 'DLOG' resource ID_MAIN and putting +it on screen in the frontmost position. Next, we go into a loop, +calling Dlg.ModalDialog() to wait for the next user +action. ModalDialog() will return us the item number that +the user has clicked on (or otherwise activated). It will handle a few +slightly more complicated things also, like the user typing into +simple textfields, but it will not do things like updating +the physical appearance of radio buttons, etc. See Inside Mac or +another programming guide for how to handle this +yourself. Fortunately, our simple application doesn't have to bother +with this, since buttons are the only active elements we have. So, we +do a simple switch on item number and call the appropriate routine to +implement the action requested. Upon the user pressing "quit" we +simply leave the loop and, hence, do_dialog(). This will +cause the python dialog object my_dlg to be deleted and +the on-screen dialog to disappear.

+ +Time for a warning: be very careful what +you do as long as a dialog is on-screen. Printing something, for +instance, may suddenly cause the standard output window to appear over +the dialog, and since we took no measures to redraw the dialog it will +become very difficult to get out of the dialog. Also, command-period +may or may not work in this situation. I have also seen crashes in +such a situation, probably due to the multiple event loops involved or +some oversight in the interpreter. You have been warned.

+ +The implementation of the "update status" command can use a bit more +explaining: we get the new information with do_status() +but now we have to update the on-screen dialog to present this +information to the user. The GetDialogItem() method of +the dialog returns three bits of information about the given item: its +type, its data handle and its rect (the on-screen x,y,w,h +coordinates). We are only interested in the data handle here, on which +we call SetDialogItemText() to set our new text. Note +here that python programmers need not bother with the C-string versus +pascal-string controversy: the python glue module knows what is needed +and converts the python string to the correct type.

+ +Finally, the three implementation routines do_connect(), +do_disconnect() and do_status() are simply +boring wrappers around the corresponding interslip methods that will +put up a dialog in case of an error.

+ +And that concludes our first example of the use of resources and +dialogs. Next, you could have a look at the source of EasyDialogs for +some examples of using input fields and filterprocs. Or, go on with +reading the second part of this document +to see how to implement a better version of this application. Not only +will it allow the user to go back to the finder (or other apps) when +your application is running, it will also free her of the RSI-inducing +chore of pressing "update status" continuously...

+ + diff --git a/Mac/Demo/example2.html b/Mac/Demo/example2.html new file mode 100644 index 0000000..3bd2791 --- /dev/null +++ b/Mac/Demo/example2.html @@ -0,0 +1,180 @@ +Using python to create Macintosh applications, part two + +

Using python to create Macintosh applications, part two

+
+ +In this document we rewrite the application of the previous example to use modeless dialogs. We +will use an application framework, and we will have a look at creating +applets, standalone applications written in Python. Source and resource file (in +binary and BinHex +form for downloading) are available in the folder example2. If you want to run the program on your +machine you will also need a new copy of FrameWork.py, which has been +updated since the 1.3 release.

+ +Again, we start with ResEdit to create our dialogs. Not only do we +want a main dialog this time but also an "About" dialog, and we +provide the BNDL resource and related stuff that +an application cannot be without. (Actually, a python applet can be +without, see below). "Inside Mac" or various +books on macintosh programming will help here. Also, you can refer to +the resource files provided in the Python source distribution for some +of the python-specific points of BNDL programming: the +"appletbundle.rsrc" file is what is used for creating applets if you +don't provide your own resource file.

+ +Let's have a look at InterslipControl-2.rsrc, our resource file. First +off, there's the standard BNDL combo. I've picked 'PYTi' as signature +for the application. I tend to pick PYT plus one lower-case letter for +my signatures. The finder gets confused if you have two applications +with the same signature. This may be due to some incorrectness on the +side of "mkapplet", I am not sure. There is one case when you +definitely need a unique signature: when you create an applet that has +its own data files and you want the user to be able to start your +applet by double-clicking one of the datafiles.

+ +There's little to tell about the BNDL stuff: I basically copied the +generic Python applet icons and pasted in the symbol for +InterSLIP. The two dialogs are equally unexciting: dialog 512 is our +main window which has four static text fields (two of which we will be +modifying during runtime, to show the status of the connection) and +two buttons "connect" and "disconnect". The "quit" and "update status" +buttons have disappeared, because they are handled by a menu choice +and automatically, respectively.

+ +

A modeless dialog application using FrameWork

+ +On to the source code in InterslipControl-2.py. The +start is similar to our previous example program InterSlipControl-1.py, with +one extra module being imported. To make life more simple we will use +the FrameWork module, a nifty piece of code that handles +all the gory mac details of event loop programming, menubar +installation and all the other code that is the same for every mac +program in the world. Like most standard modules, FrameWork will run +some sample test code when you invoke it as a main program, so try it +now. It will create a menu bar with an Apple menu with the about box +and a "File" menu with some pythonesque choices (which do nothing +interesting, by the way) and a "Quit" command that works.

+ + +A more complete description of FrameWork is +sorely needed, and will (at some point) be incorporated in the +programmers manual or in place of this paragraph. For now you'll have +to make do with the knowledge that you use FrameWork by building your +classes upon the classes provided by it and selectively overriding +methods to extend its functionality (or override the default +behaviour). And you should read the Source, of Course:-)

+ + +After the imports we get the definitions of resource-IDs in our +resource file, slightly changed from the previous version of our +program, and the state to string mapping. The main program is also +similar to our previous version, with one important exception: we +first check to see whether our resource is available before opening +the resource file. Why is this? Because later, when we will have +converted the script to an applet, our resources will be available in +the applet file and we don't need the separate resource file +anymore.

+ +Next comes the definition of our main class, +InterslipControl, which inherits +FrameWork.Application. The Application class handles the +menu bar and the main event loop and event dispatching. In the +__init__ routine we first let the base class initialize +itself, then we create our modeless dialog and finally we jump into +the main loop. The main loop continues until self is +raised, which we will do when the user selects "quit". When we create +the instance of MyDialog (which inherits +DialogWindow, which inherits Window) we pass +a reference to the application object, this reference is used to tell +Application about our new window. This enables the event loop to keep +track of all windows and dispatch things like update events and mouse +clicks.

+ +The makeusermenus() method (which is called sometime +during the Application __init__ routine) creates a File +menu with a Quit command (shortcut command-Q), which will callback to +our quit() method. Quit(), in turn, raises 'self' which +causes the mainloop to terminate.

+ +Application provides a standard about box, but we override this by +providing our own do_about() method which shows an about +box from a resource as a modal dialog. This piece of code should look +familiar to you from the previous example program. That do_about is +called when the user selects About from the Apple menu is, again, +taken care of by the __init__ routine of Application.

+ +Our main object finally overrides idle(), the method +called when no event is available. It passes the call on to our dialog +object to give it a chance to update the status fields, if needed.

+ +The MyDialog class is the container for our main +window. Initialization is again done by first calling the base class +__init__ function and finally setting two local variables +that are used by updatestatus() later.

+ +Do_itemhit() is called when an item is selected in this +dialog by the user. We are passed the item number (and the original +event structure, which we normally ignore). The code is similar to the +main loop of our previous example program: a switch depending on the +item selected. Connect() and disconnect() +are again quite similar to our previous example.

+ +Updatestatus() is different, however. It is now +potentially called many times per second instead of only when the +user presses a button we don't want to update the display every time +since that would cause some quite horrible flashing. Luckily, +interslip.status() not only provides us with a state and +a message but also with a message sequence number. If neither state +nor message sequence number has changed since the last call there is +no need to update the display, so we just return. For the rest, +nothing has changed.

+ +

Creating applets

+ +Now, if you have a PowerPC Macintosh, let us try to turn the python +script into an applet, a standalone application. Actually, +"standalone" is probably not the correct term here, since an applet +does still depend on a lot of the python environment: the PythonCore +shared library, the Python Preferences file, the python Lib folder and +any other modules that the main module depends on. It is possible to +get rid of all these dependencies except for the dependency on +PythonCore, but at the moment that is still quite difficult so we will +ignore that possibility for now. By standalone we mean here that the +script has the look-and-feel of an application, including the ability +to have its own document types, be droppable, etc.

+ +The easiest way to create an applet is to take your source file and +drop it onto "mkapplet" (normally located in the Python home +folder). This will create an applet with the same name as your python +source with the ".py" stripped. Also, if a resource file with the same +name as your source but with ".rsrc" extension is available the +resources from that file will be copied to your applet too. If there +is no resource file for your script a set of default resources will be +used, and the applet will have the default creator 'PYTa'. The latter +also happens if you do have a resource file but without the BNDL +combo. Actually, for our example that would +have been the most logical solution, since our applet does not have +its own data files. It would have saved us hunting for an unused +creator code. The only reason for using the BNDL in this case is +having the custom icon, but that could have been done by pasting an +icon on the finder Info window, or by providing an custon icon in your +resource file and setting the "custom icon" finder bit.

+ +If you need slightly more control over the mkapplet process you can +double-click mkapplet, and you will get dialogs for source and +destination of the applet. The rest of the process, including locating +the resource file, remains the same.

+ +Note that though our example application completely bypasses the +normal python user interface this is by no means necessary. Any python +script can be turned into an applet, and all the usual features of the +interpreter still work.

+ +That's all for this example, you may now return to the +table of contents to pick another topic.

diff --git a/Mac/Demo/index.html b/Mac/Demo/index.html new file mode 100644 index 0000000..9ef4ebd --- /dev/null +++ b/Mac/Demo/index.html @@ -0,0 +1,94 @@ +Macintosh Python crash course + +

Macintosh Python crash course

+
+ +This set of documents provides an introduction to various aspects of +Python programming on the Mac. It is assumed that the reader is +already familiar with Python and, to some extent, with MacOS Toolbox +programming. Other readers may find something interesting here too, +your mileage may vary.

+ +Another set of Macintosh-savvy examples, more aimed at beginners, is +maintained by Joseph Strout, at +http://www-acs.ucsd.edu/~jstrout/python/. +

+ +The document was actually written while I was working on a "real" +project: creating a single-button application that will allow my +girlfriend to read her mail (which actually pass thry my +mailbox, so I get to read it too, but don't tell her:-) without her +having to worry about internet connections, unix commands, etc. The +application, when finished, will connect to the net using InterSLIP, +start a (pseudo-)POP server on unix using rsh and use AppleScript to +tell Eudora to connect to that server and retrieve messages.

+ + +If you want to try the examples here you will have to download some +fixes to the 1.3 distribution to your Macintosh. You need an updated +version of FrameWork.py (to +go in Lib:mac and updated project templates to go into +the PlugIns folder for PPC users. +Users of 1.3.1 or later distributions don't need these fixes.

+ + +If you are reading this document on the web and would prefer to read +it offline you can transfer the whole stuff (as a BinHexed StuffIt +archive) from here. This archive includes +the fixes mentioned in the previous paragraph.

+ +

Table of contents

+ + + +At some point in the (possibly distant) future, I will add chapters on +how to use bgen to create modules completely automatic and how to make +your Python program scriptable, but that will have to wait.

+ +


+ +Please let me know if you miss critical information in this +document. I am quite sure that I will never find the time to turn it +into a complete MacPython programmers guide (which would probably be a +400-page book instead of 5 lousy html-files), but it should contain +at least the information that is neither in the standard Python +documentation nor in Inside Mac or other Mac programmers +documentation.

+ +


+Jack Jansen, +jack@cwi.nl, 6-Mar-1996. diff --git a/Mac/Demo/plugins.html b/Mac/Demo/plugins.html new file mode 100644 index 0000000..465603c --- /dev/null +++ b/Mac/Demo/plugins.html @@ -0,0 +1,363 @@ +Creating a C extension module on the Macintosh + +

Creating a C extension module on the Macintosh

+
+ +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.

+ +

Prerequisites

+ +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 MetroWerks. 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.

+ +Next, you need a python +source distribution. There is a fixed project template that +you also need if you are going to make a dynamically loaded +module. For PowerPC development you can actually get by without a full +source distribution, using the PPC Development distribution (if I have +gotten around to putting it together by the time you read +this). You'll also need a functional python interpreter, and the +Modulator program (which lives in Tools:Modulator in the +standard source distribution). You may also find that Guido's Extending and embedding +the Python interpreter is a very handy piece of documentation. I +will skip lots of details that are handled there, like complete +descriptions of Py_ParseTuple and such utility routines, +or the general structure of extension modules.

+ +

InterSLIP and the C API to it

+ +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 InterCon. 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.

+ +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 InterslipLib.c and InterslipLib.h, 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.

+ +

Using Modulator

+ +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.

+ +First, let us look at the InterslipLib.h header file, +and see that the whole interface consists of six routines: +is_open, is_connect, +is_disconnect, is_status, +is_getconfig and is_setconfig. Our first +step will be to create a skeleton file @interslipmodule.c, 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.

+ +Why call this dummy module @interslipmodule.c and not +interslipmodule.c? 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 two mistakes in a row before you do this.

+ +On systems with the Tk windowing API for Python (currently only +unix/X11 systems, but mac support may be available when you read this) +this is extremely simple. It is actually so simple that it pays to +create the skeleton module under unix and ship the code to your +mac. You start modulator and are provided with a form in which you +fill out the details of the module you are creating.

+ +

+ +You'll need to supply a module name (interslip, in our +case), a module abbreviation (pyis, 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 is_ stripped off). Note that +we use pyis as the prefix instead of the more logical +is, 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.

+ +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.

+ +

Using Modulator without Tk

+ + +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:

+ +

+	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)
+
+ +Drop this program on the python interpreter and out will come your +skeleton module.

+ +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...

+ +

Adding a module to 68K Python

+ +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 +
+		extern void initinterslip();
+
+here. Further down the file there is an array that is initialized with +modulename/initfunction pairs. Add a line of the form +
+		{"interslip",	initinterslip},
+
+here. You may want to bracket these two lines with +
+	#ifdef USE_INTERSLIP
+	#endif
+
+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 +
+	#define USE_INTERSLIP
+
+ +Make the new interpreter and check that you can import the module, see +the methods (with "dir(interslip)") and call them.

+ +

Creating a PowerPC plugin module

+ +For PowerPC 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.

+ +Go to the "PlugIns" folder and copy the files xxmodule.µ, +xxmodule_config.h and xxmodule.µ.exp to interslipmodule.µ, +interslipmodule_config.h and interslipmodule.µ.exp, respectively. Edit +interslipmodule.µ.exp and change the name of the exported routine +"initxx" to "initinterslip". Open interslipmodule.µ with CodeWarrior, +remove the file xxmodule.c and add interslipmodule.c and make a number +of adjustments to the preferences: +

+Next, compile and link your module, fire up python and do the same +tests as for 68K python.

+ +

Getting the module to do real work

+ +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, +I have spent that half hour for you, and you can see the +results in interslipmodule.c.

+ +We add +

+	#include "InterslipLib.h"
+	#include "macglue.h"
+
+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 +interslip.open.__doc__. 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.

+ +Next, we tackle the body of pyis_open(). 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 is_open() and check the return for +an error code, in which case we raise an error: +

+	err = is_open();
+	if ( err ) {
+		PyErr_Mac(ErrorObject, err);
+		return NULL;
+	}
+
+The routine PyErr_Mac() 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).

+ +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.

+ +Building the return value is done using Py_BuildValue: +

+	return Py_BuildValue("iiO&", (int)status, (int)seqnum, PyMac_BuildStr255, message);
+
+Py_BuildValue() is a very handy routine that builds tuples according +to a format string, somewhat similar to the way printf() +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. Macglue.h 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.

+ +Pyis_getconfig() 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 +Str255 it kept we are responsible for allocating the +Str255 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.

+ +Pyis_setconfig() finally shows off +Py_ParseTuple, the companion function to +Py_BuildValue. 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 +macglue.h.

+ +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 +

+	{"open",	pyis_open,	1,	pyis_open__doc__},
+
+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 getargs(args, "(ii)", +...) you will have to put zero here. See "extending and +embedding" or possibly the getargs.c source file for details if you +need them.

+ +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 +

+	PyDict_SetItemString(d, "IDLE", PyInt_FromLong(IS_IDLE));
+
+for each of our items. Since the last bit of code in our init routine +checks for previous errors with PyErr_Occurred() and +since PyDict_SetItemString() gracefully handles the case +of NULL parameters (if PyInt_FromLong() +failed, for instance) we don't have to do error checking here. In some +other cases you may have to do error checking yourself.

+ +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 MacPython Crashcourse index to +find another topic to study.

-- cgit v0.12