Commits

Bob Ippolito committed f033479

merge down the py2app-branch

  • Participants
  • Parent commits 8c598f2
  • Branches pyobjc-ancient

Comments (0)

Files changed (271)

File Doc/C-API.html

 </pre>
 <p>Return the Objective-C method name for <code><span>sel</span></code>.</p>
 <pre>
-int PyObjCBool_Check(PyObject* obj);
-</pre>
-<p>Check if <code><span>obj</span></code> is a boolean object (either the python bool type in Python
-2.3 or the PyObjC bool type in Python 2.2)</p>
-<pre>
-PyObject* PyObjCBool_FromLong(long i);
-</pre>
-<p>Create a new bool object. This will return a Python bool object in Python 2.3
-(and later) and the PyObjC bool type in Python 2.2.</p>
-<pre>
 void PyObjC_InitSuper(struct objc_super*, Class, id);
 </pre>
 <p>Initialize the <code><span>struct</span> <span>objc_super</span></code> for use with <code><span>objc_sendMsgSuper</span></code>. Use 

File Doc/api-notes-macosx.html

 <li><a href="#class-nsdata" id="id26" name="id26">Class <code><span>NSData</span></code></a></li>
 <li><a href="#class-nsdecimalnumber-and-the-nsdecimal-type" id="id27" name="id27">Class <code><span>NSDecimalNumber</span></code> and the <code><span>NSDecimal</span></code> type</a></li>
 <li><a href="#class-nsdictionary" id="id28" name="id28">Class <code><span>NSDictionary</span></code></a></li>
-<li><a href="#class-nsfault" id="id29" name="id29">Class <code><span>NSFault</span></code></a></li>
-<li><a href="#class-nsindexset" id="id30" name="id30">Class <code><span>NSIndexSet</span></code></a></li>
-<li><a href="#class-nsinvocation" id="id31" name="id31">Class <code><span>NSInvocation</span></code></a></li>
-<li><a href="#class-nsmutablearray" id="id32" name="id32">Class <code><span>NSMutableArray</span></code></a></li>
-<li><a href="#class-nsnetservice" id="id33" name="id33">Class <code><span>NSNetService</span></code></a></li>
-<li><a href="#class-nsobject" id="id34" name="id34">Class <code><span>NSObject</span></code></a></li>
-<li><a href="#class-nsscriptobjectspecifier" id="id35" name="id35">Class <code><span>NSScriptObjectSpecifier</span></code></a></li>
-<li><a href="#class-nsset" id="id36" name="id36">Class <code><span>NSSet</span></code></a></li>
-<li><a href="#class-nsstring" id="id37" name="id37">Class <code><span>NSString</span></code></a></li>
-<li><a href="#class-nsthread" id="id38" name="id38">class <code><span>NSThread</span></code></a></li>
+<li><a href="#class-nsexception" id="id29" name="id29">Class <code><span>NSException</span></code></a></li>
+<li><a href="#class-nsfault" id="id30" name="id30">Class <code><span>NSFault</span></code></a></li>
+<li><a href="#class-nsindexset" id="id31" name="id31">Class <code><span>NSIndexSet</span></code></a></li>
+<li><a href="#class-nsinvocation" id="id32" name="id32">Class <code><span>NSInvocation</span></code></a></li>
+<li><a href="#class-nsmutablearray" id="id33" name="id33">Class <code><span>NSMutableArray</span></code></a></li>
+<li><a href="#class-nsmutablestring" id="id34" name="id34">Class <code><span>NSMutableString</span></code></a></li>
+<li><a href="#class-nsnetservice" id="id35" name="id35">Class <code><span>NSNetService</span></code></a></li>
+<li><a href="#class-nsobject" id="id36" name="id36">Class <code><span>NSObject</span></code></a></li>
+<li><a href="#class-nsscriptobjectspecifier" id="id37" name="id37">Class <code><span>NSScriptObjectSpecifier</span></code></a></li>
+<li><a href="#class-nsset" id="id38" name="id38">Class <code><span>NSSet</span></code></a></li>
+<li><a href="#class-nsstring" id="id39" name="id39">Class <code><span>NSString</span></code></a></li>
+<li><a href="#class-nsthread" id="id40" name="id40">class <code><span>NSThread</span></code></a></li>
 </ul>
 </li>
-<li><a href="#interfacebuilder-framework" id="id39" name="id39">InterfaceBuilder framework</a><ul>
-<li><a href="#class-ibobjcsourceparser" id="id40" name="id40">Class <code><span>IBObjCSourceParser</span></code></a></li>
-<li><a href="#id1" id="id41" name="id41">Class <code><span>NSView</span></code></a></li>
-<li><a href="#class-nsibobjectdata" id="id42" name="id42">Class <code><span>NSIBObjectData</span></code></a></li>
-<li><a href="#class-ibobjectcontainer" id="id43" name="id43">Class <code><span>IBObjectContainer</span></code></a></li>
-<li><a href="#class-ibxmldecoder" id="id44" name="id44">Class <code><span>IBXMLDecoder</span></code></a></li>
-<li><a href="#class-ibsplitscrollview" id="id45" name="id45">Class <code><span>IBSplitScrollView</span></code></a></li>
+<li><a href="#interfacebuilder-framework" id="id41" name="id41">InterfaceBuilder framework</a><ul>
+<li><a href="#class-ibobjcsourceparser" id="id42" name="id42">Class <code><span>IBObjCSourceParser</span></code></a></li>
+<li><a href="#id1" id="id43" name="id43">Class <code><span>NSView</span></code></a></li>
+<li><a href="#class-nsibobjectdata" id="id44" name="id44">Class <code><span>NSIBObjectData</span></code></a></li>
+<li><a href="#class-ibobjectcontainer" id="id45" name="id45">Class <code><span>IBObjectContainer</span></code></a></li>
+<li><a href="#class-ibxmldecoder" id="id46" name="id46">Class <code><span>IBXMLDecoder</span></code></a></li>
+<li><a href="#class-ibsplitscrollview" id="id47" name="id47">Class <code><span>IBSplitScrollView</span></code></a></li>
 </ul>
 </li>
-<li><a href="#preferencepanes-framework" id="id46" name="id46">PreferencePanes framework</a></li>
-<li><a href="#screensaver-framework" id="id47" name="id47">ScreenSaver framework</a><ul>
-<li><a href="#class-screensaverdefaults" id="id48" name="id48">Class <code><span>ScreenSaverDefaults</span></code></a></li>
-<li><a href="#class-screensaverview" id="id49" name="id49">Class <code><span>ScreenSaverView</span></code></a></li>
+<li><a href="#preferencepanes-framework" id="id48" name="id48">PreferencePanes framework</a></li>
+<li><a href="#screensaver-framework" id="id49" name="id49">ScreenSaver framework</a><ul>
+<li><a href="#class-screensaverdefaults" id="id50" name="id50">Class <code><span>ScreenSaverDefaults</span></code></a></li>
+<li><a href="#class-screensaverview" id="id51" name="id51">Class <code><span>ScreenSaverView</span></code></a></li>
 </ul>
 </li>
 </ul>
 <h3><a href="#id28" name="class-nsdictionary">Class <code><span>NSDictionary</span></code></a></h3>
 <p>The (undocumented) methods <code><span>getKeys:</span></code>, <code><span>getObjects:</span></code> and 
 <code><span>getObjects:andKeys:</span></code> are not supported.</p>
-<h3><a href="#id29" name="class-nsfault">Class <code><span>NSFault</span></code></a></h3>
+<h3><a href="#id29" name="class-nsexception">Class <code><span>NSException</span></code></a></h3>
+<ul>
+<li><code><span>raise:format:</span></code>, <code><span>raise:format:arguments:</span></code><p>These methods are not supported because they accept a variable number of
+arguments. Use Python's <code><span>%</span></code> operator to format the message.</p>
+</li>
+</ul>
+<h3><a href="#id30" name="class-nsfault">Class <code><span>NSFault</span></code></a></h3>
 <p>The <code><span>extraData</span></code> argument/return value for <code><span>-extraData</span></code> and 
 <code><span>setTargetClassextraData:</span></code> is represented as an integer.</p>
-<h3><a href="#id30" name="class-nsindexset">Class <code><span>NSIndexSet</span></code></a></h3>
+<h3><a href="#id31" name="class-nsindexset">Class <code><span>NSIndexSet</span></code></a></h3>
 <ul>
 <li><code><span>getIndexes:maxCount:inIndexRange:</span></code>
 The usage is:<pre>
 </pre>
 </li>
 </ul>
-<h3><a href="#id31" name="class-nsinvocation">Class <code><span>NSInvocation</span></code></a></h3>
+<h3><a href="#id32" name="class-nsinvocation">Class <code><span>NSInvocation</span></code></a></h3>
 <p>In some versions of MacOS X, NSInvocation doesn't work properly with structs
 that contain padding. Such structs are not used in the MacOS X API, but may
 be present in 3th party code. This leads to problems when <code><span>forwardInvocation:</span></code>
 is used to call a method that has such a struct as one of its arguments.</p>
-<h3><a href="#id32" name="class-nsmutablearray">Class <code><span>NSMutableArray</span></code></a></h3>
+<h3><a href="#id33" name="class-nsmutablearray">Class <code><span>NSMutableArray</span></code></a></h3>
 <ul>
 <li><code><span>sortUsingFunction:context:</span></code>, <code><span>sortUsingFunction:context:range:</span></code><p>Calling this method from Python is supported, overriding it in a subclass
 is not. This limitation will be fixed in a later version of PyObjC.</p>
 <p>The <code><span>context</span></code> can be an arbitrary python object.</p>
 </li>
 </ul>
-<h3><a href="#id33" name="class-nsnetservice">Class <code><span>NSNetService</span></code></a></h3>
+<h3><a href="#id34" name="class-nsmutablestring">Class <code><span>NSMutableString</span></code></a></h3>
+<ul>
+<li><code><span>appendFormat:</span></code><p>This method is not supported because it accepts a variable number of 
+arguments. Use Python's <code><span>%</span></code> operator to format strings.</p>
+</li>
+</ul>
+<h3><a href="#id35" name="class-nsnetservice">Class <code><span>NSNetService</span></code></a></h3>
 <ul>
 <li><code><span>addresses</span></code><p>When calling this from Python this methods returns a tuple of address info
 tuples, like the values returned by <code><span>socket.getpeeraddr()</span></code>.</p>
 </li>
 </ul>
-<h3><a href="#id34" name="class-nsobject">Class <code><span>NSObject</span></code></a></h3>
+<h3><a href="#id36" name="class-nsobject">Class <code><span>NSObject</span></code></a></h3>
 <ul>
 <li><code><span>observationInfo</span></code>, <code><span>setObservationInfo:</span></code><p>These methods can be used from Python, but the <code><span>observationInfo</span></code> is 
 represented by an integer instead of <code><span>void*</span></code>. This probably makes it
 of PyObjC.</p>
 </li>
 </ul>
-<h3><a href="#id35" name="class-nsscriptobjectspecifier">Class <code><span>NSScriptObjectSpecifier</span></code></a></h3>
+<h3><a href="#id37" name="class-nsscriptobjectspecifier">Class <code><span>NSScriptObjectSpecifier</span></code></a></h3>
 <ul>
 <li><code><span>indicesOfObjectsByEvaluatingWithContainer:count:</span></code><p>Implementing this in Python is not supported yet. We're looking for a way
 to avoid leaking the returned buffer, as we cannot return a pointer to an
 internal data-structure.</p>
 </li>
 </ul>
-<h3><a href="#id36" name="class-nsset">Class <code><span>NSSet</span></code></a></h3>
+<h3><a href="#id38" name="class-nsset">Class <code><span>NSSet</span></code></a></h3>
 <ul>
 <li><code><span>initWithObjects:</span></code>, <code><span>setWithObjects:</span></code><p>This method is not supported, use <code><span>initWithArray:</span></code> instead.</p>
 </li>
 </ul>
-<h3><a href="#id37" name="class-nsstring">Class <code><span>NSString</span></code></a></h3>
+<h3><a href="#id39" name="class-nsstring">Class <code><span>NSString</span></code></a></h3>
 <p>Objective-C strings are usually represented as instances of a subclass of
 the Python type <code><span>unicode</span></code>. It is possible to access the &quot;real&quot; Objective-C
 string by using the method <code><span>NSString</span></code>. This should only be necessary when
 the length of the string as the maximum length). This limitation will be
 lifted in a future version of the bridge.</p>
 </li>
+<li><code><span>stringWithFormat:</span></code>, <code><span>initWithFormat:</span></code>, <code><span>initWithFormat:locale:</span></code>,
+<code><span>stringByAppendingFormat:</span></code><p>These methods are not supported because they accept a variable number of 
+arguments. Use Python's <code><span>%</span></code> operator to format strings.</p>
+</li>
+<li><code><span>initWithFormat:arguments:</span></code>, <code><span>initWithFormat:locale:arguments:</span></code><p>These are also not supported, with the same workaround.</p>
+</li>
 </ul>
-<h3><a href="#id38" name="class-nsthread">class <code><span>NSThread</span></code></a></h3>
+<h3><a href="#id40" name="class-nsthread">class <code><span>NSThread</span></code></a></h3>
 <p>When you're using Python 2.3 or later it is safe to call from Objective-C to
 Python on any thread. Otherwise you must be sure that the current thread has
 acquired the GIL. This means you shouldn't use API's that will call back on
 performance impact.</p>
 </li>
 </ul>
-<h2><a href="#id39" name="interfacebuilder-framework">InterfaceBuilder framework</a></h2>
+<h2><a href="#id41" name="interfacebuilder-framework">InterfaceBuilder framework</a></h2>
 <p>I (Ronald) have not found documentation for this framework, therefore the
 following methods with a &quot;difficult&quot; signature are not supported.</p>
 <p>Please let me know if there is documentation for this framework.</p>
-<h3><a href="#id40" name="class-ibobjcsourceparser">Class <code><span>IBObjCSourceParser</span></code></a></h3>
+<h3><a href="#id42" name="class-ibobjcsourceparser">Class <code><span>IBObjCSourceParser</span></code></a></h3>
 <ul>
 <li><code><span>parseClass:</span></code></li>
 </ul>
-<h3><a href="#id41" name="id1">Class <code><span>NSView</span></code></a></h3>
+<h3><a href="#id43" name="id1">Class <code><span>NSView</span></code></a></h3>
 <ul>
 <li><code><span>objectAtPoint:rect:</span></code><p>Defined in a catagory on <code><span>NSView</span></code>.</p>
 </li>
 </ul>
-<h3><a href="#id42" name="class-nsibobjectdata">Class <code><span>NSIBObjectData</span></code></a></h3>
+<h3><a href="#id44" name="class-nsibobjectdata">Class <code><span>NSIBObjectData</span></code></a></h3>
 <ul>
 <li><code><span>restoreFromObjectDataInfo:</span></code></li>
 <li><code><span>snapshotIntoObjectDataInfo:</span></code></li>
 </ul>
-<h3><a href="#id43" name="class-ibobjectcontainer">Class <code><span>IBObjectContainer</span></code></a></h3>
+<h3><a href="#id45" name="class-ibobjectcontainer">Class <code><span>IBObjectContainer</span></code></a></h3>
 <ul>
 <li><code><span>decodeObjectToIntMapTableForKey:fromCoder:alwaysCreate:</span></code></li>
 <li><code><span>decodeObjectToObjectMapTableForKey:fromCoder:alwaysCreate:</span></code></li>
 </ul>
-<h3><a href="#id44" name="class-ibxmldecoder">Class <code><span>IBXMLDecoder</span></code></a></h3>
+<h3><a href="#id46" name="class-ibxmldecoder">Class <code><span>IBXMLDecoder</span></code></a></h3>
 <ul>
 <li><code><span>allocObjectWithClassName:</span></code></li>
 </ul>
-<h3><a href="#id45" name="class-ibsplitscrollview">Class <code><span>IBSplitScrollView</span></code></a></h3>
+<h3><a href="#id47" name="class-ibsplitscrollview">Class <code><span>IBSplitScrollView</span></code></a></h3>
 <ul>
 <li><code><span>getMinimumX:maximumX:</span></code></li>
 </ul>
-<h2><a href="#id46" name="preferencepanes-framework">PreferencePanes framework</a></h2>
+<h2><a href="#id48" name="preferencepanes-framework">PreferencePanes framework</a></h2>
 <p>This framework seems to define useful classes like <code><span>NSAuthorization</span></code> and
 <code><span>NSKeychain</span></code>, but these are not documented and some useful methods have
 a hard signature.</p>
 <p>The only documented class, <code><span>NSPreferencePane</span></code>, is fully supported.</p>
-<h2><a href="#id47" name="screensaver-framework">ScreenSaver framework</a></h2>
-<h3><a href="#id48" name="class-screensaverdefaults">Class <code><span>ScreenSaverDefaults</span></code></a></h3>
+<h2><a href="#id49" name="screensaver-framework">ScreenSaver framework</a></h2>
+<h3><a href="#id50" name="class-screensaverdefaults">Class <code><span>ScreenSaverDefaults</span></code></a></h3>
 <p>This class is fully supported.</p>
-<h3><a href="#id49" name="class-screensaverview">Class <code><span>ScreenSaverView</span></code></a></h3>
+<h3><a href="#id51" name="class-screensaverview">Class <code><span>ScreenSaverView</span></code></a></h3>
 <p>This class is fully supported.</p>
 </body>
 </html>

File Doc/classes.html

 (without any whitespace) and then replace all colons by underscores.</p>
 <p>Examples:</p>
 <pre>
-(void)myAction:(id)sender     &lt;-&gt;     def `myAction_`(self, sender)
-method:(int)x andY:y          &lt;-&gt;     def `method_andY_`(self, x, y)
+(void)myAction:(id)sender     &lt;-&gt;     def myAction_(self, sender)
+method:(int)x andY:y          &lt;-&gt;     def method_andY_(self, x, y)
 </pre>
 <p>As can be seen in the examples above, Objective-C allows you to specify
 the types of arguments and the return value, while this is not possible in
 used whenever you define a method that does not extend or override an existing
 Objective-C method in the superclass.</p>
 <p>The following Objective-C class:</p>
-<blockquote>
-<p>@interface MyClass : NSObject
+<pre>
+@interface MyClass : NSObject
 {
-}</p>
-<p>-(int)methodWithX:(int)x andY:(float)y;
--(void)myAction:(id)sender;</p>
-</blockquote>
+}
+
+-(int)methodWithX:(int)x andY:(float)y;
+-(void)myAction:(id)sender;
+</pre>
 <p>can be defined in Python like this:</p>
 <pre>
 class MyClass (NSObject):
 
-        def `methodWithX_andY_`(self, x, y):
+        def methodWithX_andY_(self, x, y):
                 pass
 
-        `methodWithX_and_Y_` = selector(`methodWithX_andY_`,
+        methodWithX_and_Y_ = selector(methodWithX_andY_,
                 signature='i@:if')
 
-        def `myAction_`(self, sender):
+        def myAction_(self, sender):
                 pass
 
-        myAction_ = selector(`myAction_`,
+        myAction_ = selector(myAction_,
                 signature='v@:')
 </pre>
 <p>The explicit selectors don't really help to increase readability, especially

File Doc/intro.html

 <li><a href="#reference-counting" id="id9" name="id9">Reference counting</a></li>
 <li><a href="#informal-protocols" id="id10" name="id10">(Informal) protocols</a></li>
 <li><a href="#cocoa-bindings" id="id11" name="id11">Cocoa Bindings</a></li>
+<li><a href="#categories" id="id12" name="id12">Categories</a></li>
 </ul>
 </li>
-<li><a href="#cocoa-for-python-programmers" id="id12" name="id12">Cocoa for Python programmers</a></li>
-<li><a href="#notes-on-specific-tasks" id="id13" name="id13">Notes on specific tasks</a><ul>
-<li><a href="#working-with-threads" id="id14" name="id14">Working with threads</a></li>
-<li><a href="#finalizers" id="id15" name="id15">Finalizers</a></li>
+<li><a href="#cocoa-for-python-programmers" id="id13" name="id13">Cocoa for Python programmers</a></li>
+<li><a href="#notes-on-specific-tasks" id="id14" name="id14">Notes on specific tasks</a><ul>
+<li><a href="#working-with-threads" id="id15" name="id15">Working with threads</a><ul>
+<li><a href="#threading-limitiations-when-using-python-2-2" id="id16" name="id16">Threading limitiations when using Python 2.2</a></li>
 </ul>
 </li>
-<li><a href="#building-applications" id="id16" name="id16">Building applications</a><ul>
-<li><a href="#pure-python-buildapp-py" id="id17" name="id17">&quot;Pure Python&quot; :  buildapp.py</a></li>
-<li><a href="#ide-approach-xcode" id="id18" name="id18">&quot;IDE approach&quot; : Xcode</a></li>
+<li><a href="#finalizers" id="id17" name="id17">Finalizers</a></li>
+</ul>
+</li>
+<li><a href="#building-applications" id="id18" name="id18">Building applications</a><ul>
+<li><a href="#pure-python-buildapp-py" id="id19" name="id19">&quot;Pure Python&quot; :  buildapp.py</a></li>
+<li><a href="#ide-approach-xcode" id="id20" name="id20">&quot;IDE approach&quot; : Xcode</a></li>
 </ul>
 </li>
 </ul>
 that is used for the array of values is passed to Objective-C as the length
 argument.</p>
 <p>XXX: Add information about <code><span>array.array</span></code> rules.</p>
-<p>XXX: We don't use the right functions for conversion to C-arrays throughout
-the bridge (yet), the information is therefore not entirely correct.</p>
 <p>When you define methods in a subclass of an Objective-C class, the bridge has
 to tell the Objective-C runtime what the signature of those methods is. The
 basic rule is that all arguments as well as the return value are objects (just
 to create and use <i>Controller</i> objects.</p>
 <p>Use <code><span>objc.accessor</span></code> to create accessor methods that conform to the 
 expectations of Cocoa Bindings.</p>
-<h2><a href="#id12" name="cocoa-for-python-programmers">Cocoa for Python programmers</a></h2>
+<h3><a href="#id12" name="categories">Categories</a></h3>
+<p>Objective-C has a mechanism for modularize a class definition, it is possible
+to add methods to an existing class in a seperate compilation unit and even
+a seperate library. This mechanism is named categories and is used to enhance
+existing classes, for splitting classes in several parts and to document
+informal protocols.</p>
+<p>An example of a category definition:</p>
+<pre>
+@interface NSObject (MyCategory)
+- (NSSize)objectFootprint;
+@end
+</pre>
+<p>This declares an additional category on <code><span>NSObject</span></code>. This category contains
+a single method.</p>
+<p>The function <code><span>objc.classAddMethods</span></code> can be used to get the same effect in
+Python:</p>
+<pre>
+def objectFootprint(self):
+        pass
+
+objc.classAddMethods(NSObject, [objectFootprint])
+</pre>
+<p>This is not very clear, PyObjC therefore also provides the following 
+mechanism, implemented on top of <code><span>objc.classAddMethods</span></code>:</p>
+<pre>
+class NSObject (objc.Category(NSObject)):
+        def objectFootprint(self):
+                pass
+</pre>
+<p>To make it clear that <code><span>objc.Category</span></code> performs a special task the name in
+the class definition must be the same as the <code><span>__name__</span></code> of the argument
+to <code><span>objc.Category</span></code>.</p>
+<h2><a href="#id13" name="cocoa-for-python-programmers">Cocoa for Python programmers</a></h2>
 <p>Cocoa frameworks are mapped onto Python packages with the same name; that is
 the classes, constants and functions from the AppKit framework are available
 after you import <code><span>AppKit</span></code> in your Python script.</p>
 <li><a href="http://www.stepwise.com/">stepwise.com</a></li>
 <li>Your local bookstore or library</li>
 </ul>
-<h2><a href="#id13" name="notes-on-specific-tasks">Notes on specific tasks</a></h2>
-<h3><a href="#id14" name="working-with-threads">Working with threads</a></h3>
+<h2><a href="#id14" name="notes-on-specific-tasks">Notes on specific tasks</a></h2>
+<h3><a href="#id15" name="working-with-threads">Working with threads</a></h3>
 <p>When you create a thread and want to use PyObjC from that thread you will
 have to create an <code><span>NSAutoreleasePool</span></code> in that thread and clean it up when
 you're done. The easiest way to that is to create an instance of that class
 bound to a local variable. If the thread is long-lived you may want to arrange
 for recycling the pool once in a while.</p>
-<p>There are some limitation w.r.t. threading. You cannot use <code><span>NSThread</span></code> to 
-create new threads, but must use the python primitives instead.</p>
+<h4><a href="#id16" name="threading-limitiations-when-using-python-2-2">Threading limitiations when using Python 2.2</a></h4>
+<p>There are some limitation w.r.t. threading when using Python 2.2. The
+combination of Python 2.3 and PyObjC is fully thread-safe.</p>
+<p>You cannot use <code><span>NSThread</span></code> to create new threads, but must use the python 
+primitives instead.</p>
 <p>You must also make sure that Objective-C only makes calls to Python from a 
 thread that owns the Python GIL (that's also the reason for not being able 
-to use <code><span>NSThread</span></code> to create new threads).  This restriction will be lifted
-in a future version of PyObjC (at least when using Python 2.3).</p>
-<h3><a href="#id15" name="finalizers">Finalizers</a></h3>
+to use <code><span>NSThread</span></code> to create new threads).</p>
+<h3><a href="#id17" name="finalizers">Finalizers</a></h3>
 <p>In Python you can use the method <code><span>__del__</span></code> to clean up resources when your
 object is garbage collected. In Objective-C/Cocoa this is done with a method 
 named <code><span>dealloc</span></code>.</p>
 <p>In PyObjC you should always use the <code><span>__del__</span></code> method, the <code><span>dealloc</span></code> method
 can safely be ignored and the bridge will complain when you try to override
 this method.</p>
-<h2><a href="#id16" name="building-applications">Building applications</a></h2>
+<h2><a href="#id18" name="building-applications">Building applications</a></h2>
 <p>There are two different ways to build applications with PyObjC. There are no
 major advantages to using either one of them, use the one that is most 
 convenient to you.</p>
-<h3><a href="#id17" name="pure-python-buildapp-py">&quot;Pure Python&quot; :  buildapp.py</a></h3>
+<h3><a href="#id19" name="pure-python-buildapp-py">&quot;Pure Python&quot; :  buildapp.py</a></h3>
 <p>PyObjC includes a copy of the <code><span>bundlebuilder</span></code> module. This module is
 part of the Python 2.3 MacPython release and offers a way to build
 distutils-style scripts for building (standalone) applications.</p>
 <p>The online documentation for <code><span>bundlebuilder</span></code> contains more information on 
 building <code><span>buildapp.py</span></code> scripts and how to invoke them. There are plenty of
 example <code><span>buildapp.py</span></code> scripts in the various <a href="../Examples/00ReadMe.txt">Examples</a> subfolders.</p>
-<h3><a href="#id18" name="ide-approach-xcode">&quot;IDE approach&quot; : Xcode</a></h3>
+<h3><a href="#id20" name="ide-approach-xcode">&quot;IDE approach&quot; : Xcode</a></h3>
 <p>PyObjC includes a number of Xcode templates that can be used to 
 build (standalone) applications. Those templates are used like any other
 Xcode template. The only non-obvious detail is that you have to

File Doc/tutorial/tutorial.html

 by default, but it is part of Apple's free Developer Tools, which can
 be gotten from <a href="http://developer.apple.com/tools">http://developer.apple.com/tools</a> as a hefty 300 MB download
 after registering (for free).</p>
+<p>This tutorial shows a number of shell commands. It uses the syntax used by
+the <code><span>bash</span></code> shell (the default shell on Mac OS X 10.3). If you're using a 
+<code><span>csh</span></code> you'll have to adapt those commands a little. In <code><span>bash</span></code> you can
+modify shell variables using the syntax <code><span>NAME=value</span></code>, in <code><span>csh</span></code> you do
+the same using <code><span>setenv</span> <span>NAME</span> <span>value</span></code>.</p>
 <h2><a name="getting-started">Getting Started</a></h2>
 <ol type="1">
 <li>Create a work directory <code><span>src</span></code>. Check which Python you have installed PyObjC
 for, by running <code><span>python</span></code> and checking that <code><span>import</span> <span>Foundation</span></code> works. If it
 does not work it could be that you have installed PyObjC for <code><span>/usr/local/python</span></code>
 but Apple's <code><span>/usr/bin/python</span></code> comes first in your <code><span>$PATH</span></code>. Make sure you
-use the right python whereever it says <code><span>python</span></code> in this tutorial.<p>For convenience, set a shell variable PYLIB
-to the Python Lib directory. For MacPython-2.3 this will be:</p>
-<pre>
-$ setenv PYLIB /Library/Frameworks/Python.framework/Versions/Current/lib/python2.3
-</pre>
-<p>or if you use bash as your shell:</p>
-<pre>
-$ export PYLIB=/Library/Frameworks/Python.framework/Versions/Current/lib/python2.3
-</pre>
-<p>For Apple's <code><span>/usr/bin/python</span></code> set the variable to <code><span>/usr/lib/python2.2</span></code>,
-if you are running on MacOS X set it to <code><span>/System/Library/Frameworks/Python.framework/Versions/Current/lib/python2.3</span></code>.</p>
-</li>
+use the right python whereever it says <code><span>python</span></code> in this tutorial.</li>
 <li>Start Interface Builder, select <i>Cocoa Application</i>
 in the new file dialog, save this file as <code><span>src/MainMenu.nib</span></code>.</li>
 <li>Proceed with the instructions as lined out in Apple's
 When invoked as a main program from the command line <code><span>NibClassBuilder</span></code> will
 parse the NIB file and create a skeleton module for you. Invoke
 it as follows (from the <code><span>src</span></code> directory):<pre>
-$ python $PYLIB/site-packages/PyObjC/PyObjCTools/NibClassBuilder.py \
-        MainMenu.nib &gt; CurrencyConverter.py
+$ NCB=&quot;`python -c 'from PyObjCTools import NibClassBuilder; print NibClassBuilder.__file__'`&quot;
+$ python &quot;${NCB}&quot; MainMenu.nib &gt; CurrencyConverter.py
 </pre>
 <p>The result of this can be seen in <a href="step4-CurrencyConverter.py">step4-CurrencyConverter.py</a>.</p>
 </li>
 <li>Now we need to create an application framework around our program. Again,
 in the future it could well be that this step is not needed during 
 development, or that it becomes simpler, but for now we do the following:<pre>
-$ python $PYLIB/site-packages/PyObjC/bundlebuilder.py --link --nib=MainMenu \
-        --mainprogram=CurrencyConverter.py --resource=MainMenu.nib build
-</pre>
-<p>If you are using Python 2.3 the script is located in <code><span>plat-mac</span></code> instead
-of <code><span>site-packages</span></code> and the command is:</p>
-<pre>
-$ python2.3 $PYLIB/plat-mac/bundlebuilder.py --link --nib=MainMenu \
+$ BB=&quot;`python -c 'import bundlebuilder; print bundlebuilder.__file__'`&quot;
+$ python &quot;${BB}&quot; --link --nib=MainMenu \
         --mainprogram=CurrencyConverter.py --resource=MainMenu.nib build
 </pre>
 <p>There are a few things to note:</p>

File Examples/00ReadMe.html

 <body>
 <h2>PyObjC Examples</h2>
 <h2><a name="simple-scripts-that-demo-the-core-modules">Simple scripts that demo the core modules</a></h2>
+<p>The directory <a href="Scripts">Scripts</a> contains a number of simple command-line scripts that make use
+of Cocoa features.</p>
+<ul>
+<li><a href="Scripts/exportBook.py">exportBook.py</a><p>An example of using the AddressBook framework, this script exports some of the information
+about people in your addressbook to a CSV file.</p>
+</li>
+</ul>
+<ul>
+<li><a href="Scripts/autoreadme.py">autoreadme.py</a><p>This script implements an autorun feature for Mac OS X: it will open the ReadMe file in
+the root of every removable medium that is inserted when this script is running.</p>
+</li>
+</ul>
 <ul>
 <li>subclassing-objective-c.py<p>Create a subclass of an objective-C class</p>
 </li>
 <li><a href="ClassBrowser">ClassBrowser</a><p>A simple class browser, demonstrating the use of NSBrowser (a &quot;column view&quot;
 hierarchical widget) and NSTableView.</p>
 </li>
+<li>CocoaBindings<p>This directory contains a number of examples that make use of Key-Value Coding and
+Cocoa Bindings. These scripts require Mac OS X 10.3 or later.</p>
+<ul>
+<li>TableModel<p>Shows how to fill an NSTableView using Key-Value Coding.</p>
+</li>
+<li>TableModelWithSearch<p>A more advanced example of Key-Value Coding. This uses a custom 
+<code><span>NSArrayController</span></code>.</p>
+</li>
+</ul>
+</li>
 <li><a href="CurrencyConverter">CurrencyConverter</a><p>A simple NIB based application. Start with this one. Also see the PyObjC
 tutorial.</p>
 </li>
 NSView. Additionally shows how easy it is to embed a view in an
 NSScrollView, as well as how to use an NSColorWell.</p>
 </li>
+<li><a href="EnvironmentPrefs">EnvironmentPrefs</a><p>Another NSPreferencePane. This one can be used to edit the default environment
+for the current user. It also is a simple example of a localized application.</p>
+</li>
+<li><a href="FieldGraph">FieldGraph</a><p>Another Project Builder Cocoa project, it also includes a <code><span>buildapp.py</span></code> 
+script. This shows an simple example of an MVC based application, that also
+makes use of NSBezierPaths.</p>
+<p>The application calculates the field pattern and RMS field of an antenna 
+array with up to three elements.</p>
+</li>
 <li><a href="iClass">iClass</a><p>A more elaborate class browser; demonstrates NSOutlineView and NSTableView.</p>
 </li>
 <li><a href="PrefPane">PrefPane</a><p>Demonstrates how to write an NSPreferencePane, for use in the
 System Preferences application. Requires a framework build of Python.</p>
 </li>
-<li><a href="EnvironmentPrefs">EnvironmentPrefs</a><p>Another NSPreferencePane. This one can be used to edit the default environment
-for the current user. It also is a simple example of a localized application.</p>
+<li><a href="OpenGLDemo">OpenGLDemo</a><p>A simple program that shows how to use OpenGL in a Cocoa program.  It is a 
+port of Apple's &quot;CocoaGL&quot; example.  Note that this requires <a href="http://pyopengl.sourceforge.net/">PyOpenGL</a> to 
+be installed.</p>
+</li>
+<li><a href="PackageManager">PackageManager</a><p>An implementation of the MacPython PackageManager application using
+Cocoa.</p>
+</li>
+<li><a href="PyDocURLProtocol">PyDocURLProtocol</a><p>This example implements a subclass of <code><span>NSURLProtocol</span></code> that can be used
+to load the pydoc documentation of a module.</p>
+<p>It also includes a simple documentation browser using <code><span>WebKit</span></code> and the 
+<code><span>PyDocURLProtocol</span></code> class.</p>
+</li>
+<li><a href="PyInterpreter">PyInterpreter</a><p>A full featured embedded Python interpreter.  This demonstrates
+more complicated uses of NSTextView, manual event dispatching,
+and the new text completion feature of OS X 10.3.</p>
 </li>
 <li><a href="PythonBrowser">PythonBrowser</a><p>A reusable Python object browser, demonstrating the use of NSOutlineView
 as well as how to use an NSWindowController subclass to create a window
 from a menu action.</p>
 </li>
+<li><a href="SillyBallsSaver">SillyBallsSaver</a><p>A simple screensaver written in Python. This example requires a framework
+install of Python, that is either MacOS X 10.3 or a MacPython 2.3 
+installation.</p>
+</li>
+<li><a href="SimpleService">SimpleService</a><p>Shows how to implement entries for the Services menu.</p>
+</li>
 <li><a href="TableModel">TableModel</a><p>Basic demo that shows how to use a NSTableView.</p>
 </li>
 <li><a href="TinyTinyEdit">TinyTinyEdit</a><p>A minimal Document-based text editor application.</p>
 The code is a translation into Python of an example project in 
 'Learning Cocoa' from O'Reilly</p>
 </li>
+<li><a href="Twisted">Twisted</a><p>This directory contains a number of examples that use <a href="http://www.twistedmatrix.com">Twisted (1.1 or later)</a> with
+Cocoa.</p>
+</li>
+</ul>
+<blockquote>
+<ul>
+<li>WebServicesTool<p>This is a refactor of the WebServicesTool example that is made much simpler
+by using Twisted.</p>
+</li>
+<li>WebServicesTool-ControllerLayer<p>This is a refactor of the WebServicesTool example that is made much simpler
+by using Twisted as it does not need threads. This version also uses
+NSController and therefore requires MacOS X 10.3.</p>
+</li>
+</ul>
+</blockquote>
+<ul>
 <li><a href="WebServicesTool">WebServicesTool</a><p>Another Project Builder Cocoa project.  Queries an XML-RPC enabled web
 server for the methods that it implements.  Demonstrates a more advanced
 use of an NSTableView, how to make a toolbar as well as how to use
 multi-threading.</p>
 </li>
-<li><a href="FieldGraph">FieldGraph</a><p>Another Project Builder Cocoa project, it also includes a <code><span>buildapp.py</span></code> 
-script. This shows an simple example of an MVC based application, that also
-makes use of NSBezierPaths.</p>
-<p>The application calculates the field pattern and RMS field of an antenna 
-array with up to three elements.</p>
-</li>
-<li><a href="PyInterpreter">PyInterpreter</a><p>A full featured embedded Python interpreter.  This demonstrates
-more complicated uses of NSTextView, manual event dispatching,
-and the new text completion feature of OS X 10.3.</p>
-</li>
-<li><a href="OpenGLDemo">OpenGLDemo</a><p>A simple program that shows how to use OpenGL in a Cocoa program.  It is a 
-port of Apple's &quot;CocoaGL&quot; example.  Note that this requires <a href="http://pyopengl.sourceforge.net/">PyOpenGL</a> to 
-be installed.</p>
-</li>
-<li><a href="SillyBallsSaver">SillyBallsSaver</a><p>A simple screensaver written in Python. This example requires a framework
-install of Python, that is either MacOS X 10.3 or a MacPython 2.3 
-installation.</p>
-</li>
-<li>Twisted/WebServicesTool<p>Shows how to integrate Twisted (1.1 or later) with Cocoa, it is a
-refactor of the WebServicesTool example that is made much simpler
-by using Twisted.</p>
-</li>
-<li>Twisted/WebServicesTool-ControllerLayer<p>Shows how to integrate Twisted (1.1 or later) with Cocoa, it is a
-refactor of the WebServicesTool example that is made much simpler
-by using Twisted as it does not need threads. This one also uses
-NSController and therefore requires MacOS X 10.3.</p>
-</li>
-<li>CocoaBindings/TableModel<p>Shows how to fill an NSTableView using Key-Value Coding.</p>
-<p>This example requires MacOS X 10.3 or later.</p>
-</li>
-<li>CocoaBindings/TableModelWithSearch<p>A more advanced example of Key-Value Coding. This uses a custom 
-<code><span>NSArrayController</span></code>.</p>
-<p>This example requires MacOS X 10.3 or later.</p>
-</li>
-<li>PackageManager<p>An implementation of the MacPython PackageManager application using
-Cocoa.</p>
-</li>
-<li>ProgressViewPalette<p>A, currently non-functional, example of a palette for Interface Builder.</p>
+</ul>
+<h2><a name="some-work-in-progress-examples">Some work-in-progress examples</a></h2>
+<p>The directory <code><span>NonFunctional</span></code> contains a number of examples that are not working
+for one reason or another. The most likely reason is that example relies on features
+that have not yet been implemented.</p>
+<ul>
+<li><a href="NonFunctional/ProgressViewPalette">ProgressViewPalette</a><p>An example of a palette for Interface Builder. It is a straight port of an Apple
+example with the same name.</p>
 <p>This example does not work at the moment, due to an incompatibility between
 PyObjC and the way IB looks for classes.</p>
 </li>
-<li>PyDocURLProtocol<p>This example implements a subclass of <code><span>NSURLProtocol</span></code> that can be used
-to load the pydoc documentation of a module.</p>
-<p>It also includes a simple documentation browser using <code><span>WebKit</span></code> and the 
-<code><span>PyDocURLProtocol</span></code> class.</p>
-</li>
-<li>SimpleService<p>Shows how to implement entries for the Services menu.</p>
-</li>
 </ul>
 </body>
 </html>

File Examples/ClassBrowser/buildapp.py

-from bundlebuilder import buildapp
-
-buildapp(
-        mainprogram = "ClassBrowser.py",
-        resources = ["ClassBrowser.nib"],
-        nibname = "ClassBrowser",
-)

File Examples/ClassBrowser/setup.py

+from distutils.core import setup
+import py2app
+
+plist=dict(NSMainNibFile="ClassBrowser")
+setup(
+        app = ["ClassBrowser.py"],
+        data_files = ["ClassBrowser.nib"],
+        options = dict(py2app=dict(plist=plist)),
+)

File Examples/CocoaBindings/TableModel/buildapp.py

-"""
-Script for building the example, alternative for the Xcode project.
-
-Usage:
-    python buildapp.py build
-"""
-from bundlebuilder import buildapp
-
-buildapp(
-        name = "TableModel",
-        mainprogram = "__main__.py",
-        resources = ["English.lproj", "TableModelAppDelegate.py" ],
-        nibname = "MainMenu",
-)

File Examples/CocoaBindings/TableModel/setup.py

+"""
+Script for building the example, alternative for the Xcode project.
+
+Usage:
+    python setup.py py2app
+"""
+from distutils.core import setup
+import py2app
+
+setup(
+        app = ["__main__.py"],
+        data_files = ["English.lproj"],
+        options = dict(py2app=dict(plist='Info.plist')),
+)

File Examples/CocoaBindings/TableModelWithSearch/buildapp.py

-"""
-Script for building the example, alternative for the Xcode project
-
-Usage:
-        python buildapp.py build
-"""
-from bundlebuilder import buildapp
-
-buildapp(
-        name = "TableModelWithSearch",
-        mainprogram = "__main__.py",
-        resources = [
-                "English.lproj",
-                "FilteringArrayController.py",
-                "TableModelWithSearchAppDelegate.py",
-                "ToolbarCreator.py",
-            ],
-        nibname = "MainMenu",
-)

File Examples/CocoaBindings/TableModelWithSearch/setup.py

+"""
+Script for building the example, alternative for the Xcode project
+
+Usage:
+        python setup.py py2app
+"""
+from distutils.core import setup
+import py2app
+
+setup(
+    app = ["__main__.py"],
+    data_files = ["English.lproj"],
+    options = dict(py2app=dict(plist='Info.plist')),
+)

File Examples/CurrencyConverter/buildapp.py

-#
-# Script for building the .app bundle.
-#
-# Usage:
-#   python buildapp.py build
-#
-from bundlebuilder import buildapp
-
-buildapp(
-    mainprogram = "CurrencyConverter.py",
-    resources = ["English.lproj" ],
-    nibname = "MainMenu",
-)

File Examples/CurrencyConverter/setup.py

+#
+# Script for building the .app bundle.
+#
+# Usage:
+#   python setup.py py2app
+#
+from distutils.core import setup
+import py2app
+
+setup(
+    app = ["CurrencyConverter.py"],
+    data_files = ["English.lproj"],
+)

File Examples/DotView/buildapp.py

-from bundlebuilder import buildapp
-
-buildapp(
-        name = "DotView",
-        mainprogram = "DotView.py",
-        resources = ["English.lproj"],
-        nibname = "MainMenu",
-)

File Examples/DotView/setup.py

+from distutils.core import setup
+import py2app
+
+setup(
+    app = ["DotView.py"],
+    data_files = ["English.lproj"],
+)

File Examples/FieldGraph/CGraphView.py

-from Foundation import NSObject
 from PyObjCTools import NibClassBuilder
-from objc import *
-
-from Foundation import NSData
-from AppKit import NSBezierPath, NSColor, NSRectFill, NSAffineTransform
-from AppKit import NSImage, NSCompositeSourceOver, NSCursor
+from Foundation import *
+from AppKit import *
 from math import pi, sin, cos
 from fieldMath import *
 

File Examples/FieldGraph/buildapp.py

-#! /usr/local/python
-
-import os
-
-from bundlebuilder import buildapp
-
-src = [ fn for fn in os.listdir('.') if fn.endswith('.py') and fn not in ('Main.py', 'buildapp.py') ]
-
-buildapp(
-        name = "FieldGraph",
-        mainprogram = "Main.py",
-        resources = ["English.lproj", 'CrossCursor.tiff', 'Map.png' ] + src,
-        nibname = "MainMenu",
-)

File Examples/FieldGraph/setup.py

+#!/usr/bin/env python
+from distutils.core import setup
+import py2app
+
+plist = dict(CFBundleName='FieldGraph')
+setup(
+    app = ["Main.py"],
+    data_files = ["English.lproj", 'CrossCursor.tiff', 'Map.png'],
+    options = dict(py2app=dict(plist=plist)),
+)

File Examples/NonFunctional/Twisted/Packman/MainMenu.nib/classes.nib

+{
+    IBClasses = (
+        {
+            ACTIONS = {addDb = id; }; 
+            CLASS = DatabasesController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {databaseTable = id; newDatabaseURL = id; packageController = id; }; 
+            SUPERCLASS = NSObject; 
+        }, 
+        {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {
+            ACTIONS = {filter = id; install = id; }; 
+            CLASS = PackageController; 
+            LANGUAGE = ObjC; 
+            OUTLETS = {databaseList = id; descriptionField = id; table = id; }; 
+            SUPERCLASS = NSObject; 
+        }
+    ); 
+    IBVersion = 1; 
+}

File Examples/NonFunctional/Twisted/Packman/MainMenu.nib/info.nib

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IBDocumentLocation</key>
+	<string>309 31 516 438 0 0 1440 878 </string>
+	<key>IBEditorPositions</key>
+	<dict>
+		<key>29</key>
+		<string>132 280 318 44 0 0 1440 878 </string>
+	</dict>
+	<key>IBFramework Version</key>
+	<string>349.0</string>
+	<key>IBOpenObjects</key>
+	<array>
+		<integer>29</integer>
+		<integer>21</integer>
+	</array>
+	<key>IBSystem Version</key>
+	<string>7C107</string>
+</dict>
+</plist>

File Examples/NonFunctional/Twisted/Packman/MainMenu.nib/keyedobjects.nib

Binary file added.

File Examples/NonFunctional/Twisted/Packman/PackMan.py

+
+from StringIO import StringIO
+
+## Install corefoundation reactor
+import twisted.internet.cfreactor
+reactor = twisted.internet.cfreactor.install()
+
+from twisted.internet import error
+
+## New HTTP client in Twisted/sandbox/moshez
+import newclient
+
+## ObjC stuff
+from Foundation import *
+from PyObjCTools import NibClassBuilder, AppHelper
+from objc import YES, NO, selector
+
+## plistlib from macpython
+import plistlib
+
+import newpimp
+
+# DEBUG
+import sys
+from twisted.python import log
+log.startLogging(sys.stdout)
+
+# the test-multidatabase
+DATABASE = 'http://undefined.org/python/pimp/panther.plist'
+
+NibClassBuilder.extractClasses("MainMenu")
+
+
+class DatabasesController(NibClassBuilder.AutoBaseClass):
+    _databases = [
+        {'description': "Standard", 'url': ''}
+    ]
+
+    def addDb_(self, sender):
+        if len(self._databases) is 1:
+            self._databases = self._databases[:]
+        url = self.newDatabaseURL.stringValue()
+        try:
+            self.packageController.openDatabase(str(url))
+        except ValueError, e:
+            print "bad url", `url`, e
+            return
+        self._databases.append({'description': '', 'url': url})
+        self.databaseTable.reloadData()
+        self.databaseTable.selectRowIndexes_byExtendingSelection_(
+            NSIndexSet.indexSetWithIndex_(len(self._databases)-1), NO
+        )
+
+    def numberOfRowsInTableView_(self, view):
+        return len(self._databases)
+
+    def tableView_objectValueForTableColumn_row_(self, view, column, row):
+        row = self._databases[row]
+        return row['description'] or row['url']
+
+    def tableViewSelectionDidChange_(self, aNotification):
+        pass
+
+
+class PackageController(NibClassBuilder.AutoBaseClass):
+    packages = ()
+    _allPackages = ()
+
+    def applicationDidFinishLaunching_(self, aNotification):
+        self.databaseList.selectRowIndexes_byExtendingSelection_(
+            NSIndexSet.indexSetWithIndex_(0), NO
+        )
+        self.openDatabase(DATABASE)
+        reactor.run()
+
+    def openDatabase(self, url):
+        log.msg('Opening database: %s' % (url,))
+        return newpimp.downloadPackmanDatabase(url.encode('utf8')
+        ).addCallback(
+            self.gotPlist
+        ).addErrback(
+            self.errorOpening
+        )
+
+    def gotPlist(self, plist):
+        log.msg('received plist')
+        packages = plist['Packages']
+        #
+        # XXX - Flavors and Version should be condensed into one row?
+        #
+        # default sort is
+        #   Name, Version, Flavor
+        #
+        packages.sort(lambda a,b:(
+            cmp(a['Name'].lower(), b['Name'].lower()) or
+            cmp(a.get('Version'), b.get('Version')) or
+            cmp(a.get('Flavor'), b.get('Flavor'))))
+        self._allPackagesIncludingHidden = packages
+        self._allPackages = self.packages = [x for x in packages if x.get('Version', None)]
+        self.table.reloadData()
+
+    def errorOpening(self, failure):
+        print "error opening url", failure
+
+    def install_(self, sender):
+        print "SELECTED", self.packages[self.table.selectedRow()]['Name']
+
+    def numberOfRowsInTableView_(self, view):
+        return len(self.packages)
+
+    def tableView_objectValueForTableColumn_row_(self, view, column, row):
+        columnName = column.headerCell().stringValue()
+        return getattr(self, 'column_%s' % columnName, lambda row: '')(row)
+
+    def column_Installed(self, row):
+        return "No"
+
+    def column_Package(self, row):
+        return self.packages[row]['Name']
+
+    def column_Version(self, row):
+        return self.packages[row].get('Version', 'No Version')
+
+    def tableViewSelectionDidChange_(self, aNotification):
+        row = self.table.selectedRow()
+        self.descriptionField.setStringValue_(
+            self.packages[row]['Description'].strip()
+        )
+
+    def filter_(self, sender):
+        search = sender.stringValue()
+        self.packages = [
+            package for package in self._allPackages if search.lower() in package['Name'].lower()
+        ]
+        self.table.reloadData()
+
+
+if __name__ == "__main__":
+    AppHelper.runEventLoop()

File Examples/NonFunctional/Twisted/Packman/buildapp.py

+from bundlebuilder import buildapp
+
+buildapp(
+    mainprogram = "PackMan.py",
+    resources = ["MainMenu.nib"],
+    includeModules=['newclient', 'newpimp'],
+    symlink = True,
+    nibname = "MainMenu"
+)

File Examples/NonFunctional/Twisted/Packman/newclient.py

+"""
+Example usage:
+o = opener()
+o.open("http://www.yahoo.com/").addCallback(read).addCallback(util.println)
+o.open("http://www.yahoo.com/").addCallback(download(sys.stdout.write))
+d = o.open("http://www.yahoo.com/")
+d.addCallback(download(file("yahoo.html", 'wb'))
+d.addCallback(close)
+"""
+from twisted.internet import protocol, defer
+from twisted.protocols import http
+from twisted.internet import error
+import urllib2, urlparse
+
+class Request(urllib2.Request):
+
+    def __init__(self, url, data=None, headers={}, visited=None):
+        urllib2.Request.__init__(self, url, data, headers)
+        if visited is None:
+            visited = {}
+        self.visited = visited
+
+
+class Response:
+
+    def __init__(self, version, status, message):
+        self.version = version
+        self.status = status
+        self.message = message
+        self.headers = {}
+        self._events = []
+
+    def handleHeader(self, key, value):
+        self.headers.setdefault(key.lower(), []).append(value)
+
+    def setHandler(self, dataReceived, connectionLost, dataDone):
+        self.dataReceived = dataReceived
+        self.connectionLost = connectionLost
+        self.dataDone = dataDone
+        for event in self._events:
+            getattr(self, event[0])(*event[1:])
+        del self._events
+
+    def dataReceived(self, data):
+        self._events.append(('dataReceived', data))
+    def connectionLost(self, reason):
+        self._events.append(('connectionLost', reason))
+    def dataDone(self):
+        self._events.append(('dataDone',))
+
+class HTTPClient(http.HTTPClient):
+
+    response = None
+    def connectionMade(self):
+        self.factory.connection.callback(self)
+
+    def setResponseSink(self, deferred):
+        self.sink = deferred
+
+    def handleStatus(self, version, status, message):
+        self.response = Response(version, status, message)
+        self.handleHeader = self.response.handleHeader
+
+    def handleEndHeaders(self):
+        self.sink.callback(self.response)
+        def _(reason):
+            http.HTTPClient.connectionLost(self, reason)
+            if not self.done:
+                self.response.connectionLost(reason)
+        self.connectionLost = _
+
+    def connectionLost(self, reason):
+        http.HTTPClient.connectionLost(self, reason)
+        self.sink.errback(reason)
+
+    def handleResponsePart(self, data):
+        self.response.dataReceived(data)
+
+    def handleResponseEnd(self):
+        self.done = 1
+        if self.response is not None:
+            self.response.dataDone()
+
+class ClientFactory(protocol.ClientFactory):
+    protocol = HTTPClient
+    noisy = None and "FOR GOD'S SAKE NOISY CLIENT FACTORIES SUCK"
+    def __init__(self):
+        self.connection = defer.Deferred()
+    def connectionFailed(self, _, reason):
+        self.connection.errback(reason)
+
+class NoHandler(LookupError):
+    pass
+
+def sendHeaders(connection, req):
+    if req.has_data():
+        data = req.get_data()
+        connection.sendCommand('POST', requ.get_selector())
+        if not req.headers.has_key('Content-type'):
+            connection.sendHeader('Content-type',
+                                  'application/x-www-form-urlencoded')
+        if not req.headers.has_key('Content-length'):
+            connection.sendHeader('Content-length', str(len(data)))
+    else:
+        connection.sendCommand('GET', req.get_selector())
+    connection.sendHeader('Host', req.get_host())
+    for el in req.headers.iteritems():
+        connection.sendHeader(*el)
+    connection.endHeaders()
+    if req.has_data():
+        connection.transport.write(data)
+    return connection
+
+class Opener:
+
+    def __init__(self, *handlers):
+        self.handlers = list(handlers)
+        for handler in self.handlers:
+            handler.setOpener(self)
+
+    def callMethod(self, name, *args, **kw):
+        value = None
+        for handler in self.handlers:
+            value = getattr(handler, name, lambda *args,**kw:None)(*args, **kw)
+            if value is not None:
+                break
+        if value is None:
+            raise NoHandler("No handlers found for method %s" % name)
+        return value
+
+    def open(self, request, factory=None):
+        try:
+            self.callMethod('transform', request)
+        except NoHandler:
+            pass
+        factory = ClientFactory()
+        self.callMethod('connect_'+request.get_type(), request, factory)
+        d = factory.connection
+        d.addCallback(sendHeaders, request)
+        def _(connection):
+            d = defer.Deferred()
+            connection.setResponseSink(d)
+            return d
+        d.addCallback(_)
+        def _(response):
+            try:
+                response = self.callMethod('response_%s' % response.status,
+                                           response, request)
+            except NoHandler:
+                response = self.callMethod('responseDefault', response, request)
+            return response
+        d.addCallback(_)
+        return d
+
+class BaseHandler:
+
+    def setOpener(self, parent):
+        self.parent = parent
+
+HTTP_PORT = 80
+HTTPS_PORT = 443
+
+class BaseHTTPHandler(BaseHandler):
+
+    def connect_http(self, request, factory):
+        from twisted.internet import reactor
+        host, port = urllib2.splitport(request.get_host())
+        if port is None:
+            port = HTTP_PORT
+        reactor.connectTCP(host, port, factory)
+        return 1 # handled
+
+    def connect_https(self, request, factory):
+        from twisted.internet import reactor
+        host, port = urllib2.splitport(request.get_host())
+        if port is None:
+            port = HTTPS_PORT
+        reactor.connectSSL(host, port, factory)
+        return 1 # handled
+
+    def responseDefault(self, response, request):
+        raise response
+
+    def response_200(self, response, request):
+        return response
+
+class HTTPRedirect(BaseHandler):
+
+    def response_301(self, response, req):
+        h = response.headers
+        url = (h.get('location') or h.get('uri') or [None])[0]
+        if url is None:
+            raise response
+        url = urlparse.urljoin(req.get_full_url(), url)
+        if len(req.visited)>10 or url in req.visited:
+            raise ValueError("redirection loop detected", url)
+        return self.parent.open(Request(url, req.get_data(),
+                                        req.headers, req.visited))
+
+    response_307 = response_302 = response_301
+
+    def response_303(self, response, req):
+        req.add_data(None)
+        return response_302(self, response, req)
+
+
+class ProxyHandler(BaseHandler):
+
+    def __init__(self, proxies=None):
+        if proxies is None:
+            proxies = urllib2.getproxies()
+        self.proxies = proxies
+
+    def connect_http(self, request, factory):
+        origType = request.get_type()
+        proxy = self.proxies.get(origType)
+        if not proxy:
+            return
+        type, url = urllib2.splittype(proxy)
+        host, _ = urllib2.splithost(url)
+        if '@' in host:
+            user, host = host.split('@', 1)
+            user = base64.encodestring(unquote(user_pass)).strip()
+            request.add_header('Proxy-Authorization', 'Basic '+user)
+        host = unquote(host)
+        request.set_proxy(host, type)
+        if origType == type:
+            return
+        else:
+            return self.callMethod('connect_'+request.type, request, factory)
+
+    connect_https = connect_http
+
+def opener():
+    return Opener(ProxyHandler(), BaseHTTPHandler(), HTTPRedirect())
+
+def read(response):
+    d, l = defer.Deferred(), []
+    done = []
+    def dataDone():
+        if not done:
+            d.callback(''.join(l))
+            done.append(True)
+    response.setHandler(
+        dataReceived=l.append,
+        dataDone=dataDone,
+        connectionLost=d.errback,
+    )
+    return d
+
+def download(fp):
+    def _(response):
+        d = defer.Deferred()
+        response.setHandler(
+            dataReceived=fp.write,
+            dataDone=lambda:d.callback(fp),
+            connectionLost=d.errback,
+        )
+        return d
+    return _
+
+def close(fp):
+    fp.close()
+
+def urlopen(url, data=None):
+    return opener().open(Request(url, data))
+
+def urlretrieve(name, url, data=None):
+    d = opener().open(Request(url, data))
+    d.addCallback(download(file(name, 'wb')))
+    d.addCallback(close)
+    return d
+
+if __name__ == '__main__':
+    from twisted.internet import reactor
+    import sys
+    opener().open(Request('http://www.yahoo.com/')
+    ).addCallback(download(sys.stdout))
+    reactor.run()

File Examples/NonFunctional/Twisted/Packman/newpimp.py

+"""Package Install Manager for Python.
+
+This is currently a MacOSX-only strawman implementation.
+Despite other rumours the name stands for "Packman IMPlementation".
+
+Tools to allow easy installation of packages. The idea is that there is
+an online XML database per (platform, python-version) containing packages
+known to work with that combination. This module contains tools for getting
+and parsing the database, testing whether packages are installed, computing
+dependencies and installing packages.
+
+There is a minimal main program that works as a command line tool, but the
+intention is that the end user will use this through a GUI.
+"""
+import sys
+import os
+import popen2
+import urllib
+import urllib2
+import urlparse
+import plistlib
+import distutils.util
+import distutils.sysconfig
+import md5
+import tarfile
+import tempfile
+import shutil
+
+import newclient
+from twisted.internet import defer, error
+from twisted.python import log
+from cStringIO import StringIO
+
+
+__all__ = ["PimpPreferences", "PimpDatabase", "PimpPackage", "main",
+    "PIMP_VERSION", "main"]
+
+_scriptExc_NotInstalled = "pimp._scriptExc_NotInstalled"
+_scriptExc_OldInstalled = "pimp._scriptExc_OldInstalled"
+_scriptExc_BadInstalled = "pimp._scriptExc_BadInstalled"
+
+NO_EXECUTE=0
+
+PIMP_VERSION="0.3"
+
+# Flavors:
+# source: setup-based package
+# binary: tar (or other) archive created with setup.py bdist.
+DEFAULT_FLAVORORDER=['source', 'binary']
+DEFAULT_DOWNLOADDIR='/tmp'
+DEFAULT_BUILDDIR='/tmp'
+DEFAULT_INSTALLDIR=distutils.sysconfig.get_python_lib()
+DEFAULT_PIMPDATABASE="http://www.python.org/packman/version-0.3/%s.plist" % distutils.util.get_platform()
+
+def _cmd(output, dir, *cmditems):
+    """Internal routine to run a shell command in a given directory."""
+
+    cmd = ("cd \"%s\"; " % dir) + " ".join(cmditems)
+    if output:
+        output.write("+ %s\n" % cmd)
+    if NO_EXECUTE:
+        return 0
+    child = popen2.Popen4(cmd)
+    child.tochild.close()
+    while 1:
+        line = child.fromchild.readline()
+        if not line:
+            break
+        if output:
+            output.write(line)
+    return child.wait()
+
+class PimpUnpacker:
+    """Abstract base class - Unpacker for archives"""
+
+    _can_rename = False
+
+    def __init__(self, argument,
+            dir="",
+            renames=[]):
+        self.argument = argument
+        if renames and not self._can_rename:
+            raise RuntimeError, "This unpacker cannot rename files"
+        self._dir = dir
+        self._renames = renames
+
+    def unpack(self, archive, output=None, package=None):
+        return None
+
+class PimpCommandUnpacker(PimpUnpacker):
+    """Unpack archives by calling a Unix utility"""
+
+    _can_rename = False
+
+    def unpack(self, archive, output=None, package=None):
+        cmd = self.argument % archive
+        if _cmd(output, self._dir, cmd):
+            return "unpack command failed"
+
+def downloadPackmanDatabase(url):
+    visited = {}
+    opener = newclient.opener()
+    def _mergePackages(dlist, main):
+        if 'Packages' not in main:
+            main['Packages'] = []
+        packages = main['Packages']
+        for subplist in dlist:
+            packages.extend(subplist.get('Packages', ()))
+        return main
+
+    def _gotPlist(plist, url, visited=visited):
+        if plist.get('Version', None) != PIMP_VERSION:
+            raise ValueError, "PackMan version %s != %s" % (plist['Version'], PIMP_VERSION)
+        visited[url] = url
+        plist.setdefault('Packages', [])
+        others = plist.setdefault('Include', [])
+        if not others:
+            return plist
+        for url in others:
+            if url in visited:
+                raise ValueError, "Circular reference for %s" % (url,)
+        deferreds = map(_download, others)
+        def _preenList(dl):
+            rval = []
+            for (success, result) in dl:
+                if not success:
+                    return result
+                rval.append(result)
+            return rval
+        return defer.DeferredList(deferreds, fireOnOneErrback=True
+            ).addCallback(
+                _preenList
+            ).addCallback(
+                _mergePackages, plist
+            )
+
+    def _download(url):
+        return opener.open(
+            newclient.Request(url)
+        ).addCallback(
+            newclient.read
+        ).addCallback(
+            StringIO
+        ).addCallback(
+            plistlib.Plist.fromFile
+        ).addCallback(
+            _gotPlist, url
+        )
+
+    return _download(url)
+
+class PimpTarUnpacker(PimpUnpacker):
+    """Unpack tarfiles using the builtin tarfile module"""
+
+    _can_rename = True
+
+    def unpack(self, archive, output=None, package=None):
+        tf = tarfile.open(archive, "r")
+        members = tf.getmembers()
+        skip = []
+        if self._renames:
+            for member in members:
+                for oldprefix, newprefix in self._renames:
+                    if oldprefix[:len(self._dir)] == self._dir:
+                        oldprefix2 = oldprefix[len(self._dir):]
+                    else:
+                        oldprefix2 = None
+                    if member.name[:len(oldprefix)] == oldprefix:
+                        if newprefix is None:
+                            skip.append(member)
+                            #print 'SKIP', member.name
+                        else:
+                            member.name = newprefix + member.name[len(oldprefix):]
+                            print '    ', member.name
+                        break
+                    elif oldprefix2 and member.name[:len(oldprefix2)] == oldprefix2:
+                        if newprefix is None:
+                            skip.append(member)
+                            #print 'SKIP', member.name
+                        else:
+                            member.name = newprefix + member.name[len(oldprefix2):]
+                            #print '    ', member.name
+                        break
+                else:
+                    skip.append(member)
+                    #print '????', member.name
+        for member in members:
+            if member in skip:
+                continue
+            tf.extract(member, self._dir)
+        if skip:
+            names = [member.name for member in skip if member.name[-1] != '/']
+            if package:
+                names = package.filterExpectedSkips(names)
+            if names:
+                return "Not all files were unpacked: %s" % " ".join(names)
+
+ARCHIVE_FORMATS = [
+    (".tar.Z", PimpTarUnpacker, None),
+    (".taz", PimpTarUnpacker, None),
+    (".tar.gz", PimpTarUnpacker, None),
+    (".tgz", PimpTarUnpacker, None),
+    (".tar.bz", PimpTarUnpacker, None),
+    (".zip", PimpCommandUnpacker, "unzip \"%s\""),
+]
+
+class PimpPreferences:
+    """Container for per-user preferences, such as the database to use
+    and where to install packages."""
+
+    def __init__(self,
+            flavorOrder=None,
+            downloadDir=None,
+            buildDir=None,
+            installDir=None,
+            pimpDatabase=None):
+        if not flavorOrder:
+            flavorOrder = DEFAULT_FLAVORORDER
+        if not downloadDir:
+            downloadDir = DEFAULT_DOWNLOADDIR
+        if not buildDir:
+            buildDir = DEFAULT_BUILDDIR
+        if not pimpDatabase:
+            pimpDatabase = DEFAULT_PIMPDATABASE
+        self.setInstallDir(installDir)
+        self.flavorOrder = flavorOrder
+        self.downloadDir = downloadDir
+        self.buildDir = buildDir
+        self.pimpDatabase = pimpDatabase
+
+    def setInstallDir(self, installDir=None):
+        if installDir:
+            # Installing to non-standard location.
+            self.installLocations = [
+                ('--install-lib', installDir),
+                ('--install-headers', None),
+                ('--install-scripts', None),
+                ('--install-data', None)]
+        else:
+            installDir = DEFAULT_INSTALLDIR
+            self.installLocations = []