Monday, October 23, 2006

Skype API on OS X

Well here's my code so far. It uses pyobjc to fetch the Skype Public API framework and then defines a class to be used as a delegate for the Skype API. Skype is supposed to call the skypeAttachResponse() callback, but that does not happen.

BTW, note the magic in the objc.loadBundle() call ....

Sending messages to the Skype API then results in a log message stating that no client is attached. Doh.

Calling isSkypeRunning() et al works, so I'm actually pretty sure that I'm talking to the Skype API ;)

Any pointers would be greatly appreciated ;)

Update: I noticed that the signature of the callback methods of my Python Delegator class is probably not correct. I'll investigate further. There are pointers to that inside the PyObjC docs.

import objc
from Foundation import *

objc.loadBundle( "SkypeAPI", globals(),
bundle_path=objc.pathForFramework( \
"
~/Library/Frameworks/Skype.framework" ) )

#objc.setSignatureForSelector( "SkypeAPI",
# "sendSkypeCommand:", "v@N^@:@" )

SkypeAPIDelegate = objc.protocolNamed( "SkypeAPIDelegate" )

class Delegate(NSObject, SkypeAPIDelegate):
def init(self, app_name):
self = super( Delegate, self).init()
if self is None:
return None

self._appname = app_name

return self

def clientApplicationName( self ):
return self._appname

def skypeAttachResponse( self, code ):
NSLog( "skypeAttachResponse: %d" % ( code ) )

def skypeNotificationReceived( self, nsstring ):
NSLog( "skypeNotificationReceivest: %s" % ( nsstring ) )

def skypeBecameAvailable( self, notification ):
NSLog( "skypeBecameAvailable: %s" % ( notification ) )

def skypeBecameUnavailable( self, notification ):
NSLog( "skypeBecameUnavailable: %s" % ( notification ) )

def do_connect():
delegate = Delegate.alloc().init( "testapp" )

NSLog( "connecting to skype" )
SkypeAPI.setSkypeDelegate_( delegate )
SkypeAPI.connect()

NSLog( "sending" )
SkypeAPI.sendSkypeCommand_( u"PROTOCOL 1" )

def main():
import sys
from PyObjCTools import AppHelper

NSLog( "running event loop" )
AppHelper.callLater( 5, do_connect )
AppHelper.runConsoleEventLoop( installInterrupt=True )

if __name__ == "__main__":
main()

# vim: set ts=4 sw=4 expandtab :


Note: I love Vim ;)

Embedding Python and Multithreading

Just found this post on comp.lang.python about an article detailing the hoops one needs to jump through when it comes to embedding python and you have a multithreaded application.

Note that all that still means that you can't have one Python interpreter running in multiple threads. If I understood things as they are, Python currently needs a 1:1 relationship between Python interpreters and threads. That is, you can have more than one Python interpreter per process but only one per thread. You can have more than one interpreter per process if you really want, but these threads then compete against the same GIL.

This is due to the fact that the standard CPython implementation uses a so-called global interpreter lock (GIL) to serialize access to the internal Python API. There's one GIL per interpreter.

Also note that there's hidden pitfall here: The interpreter periodically (every n byte code instructions) yields execution and releases the GIL. That means if other threads pend on the GIL they might get runnable, which is perhaps not what one might expect.

Calling C extension is another common way to release the GIL, such that a expensive calculation in does not block other threads.