Commits

Anonymous committed 1f0efb2

move growl-python up a directory

Comments (0)

Files changed (8)

growl-python/Growl.py

+"""
+A Python module that enables posting notifications to the Growl daemon.
+See <http://growl.info/> for more information.
+"""
+__version__ = "0.7" 
+__author__ = "Mark Rowe <bdash@users.sourceforge.net>"
+__copyright__ = "(C) 2003 Mark Rowe <bdash@users.sourceforge.net>. Released under the BSD license."
+__contributors__ = ["Ingmar J Stein (Growl Team)", 
+                    "Rui Carmo (http://the.taoofmac.com)",
+                    "Jeremy Rossi <jeremy@jeremyrossi.com>",
+                    "Peter Hosey <http://boredzo.org/> (Growl Team)",
+                   ]
+
+import _growl
+import types
+import struct
+import md5
+import socket
+
+GROWL_UDP_PORT=9887
+GROWL_PROTOCOL_VERSION=1
+GROWL_TYPE_REGISTRATION=0
+GROWL_TYPE_NOTIFICATION=1
+
+GROWL_APP_NAME="ApplicationName"
+GROWL_APP_ICON="ApplicationIcon"
+GROWL_NOTIFICATIONS_DEFAULT="DefaultNotifications"
+GROWL_NOTIFICATIONS_ALL="AllNotifications"
+GROWL_NOTIFICATIONS_USER_SET="AllowedUserNotifications"
+
+GROWL_NOTIFICATION_NAME="NotificationName"
+GROWL_NOTIFICATION_TITLE="NotificationTitle"
+GROWL_NOTIFICATION_DESCRIPTION="NotificationDescription"
+GROWL_NOTIFICATION_ICON="NotificationIcon"
+GROWL_NOTIFICATION_APP_ICON="NotificationAppIcon"
+GROWL_NOTIFICATION_PRIORITY="NotificationPriority"
+        
+GROWL_NOTIFICATION_STICKY="NotificationSticky"
+
+GROWL_APP_REGISTRATION="GrowlApplicationRegistrationNotification"
+GROWL_APP_REGISTRATION_CONF="GrowlApplicationRegistrationConfirmationNotification"
+GROWL_NOTIFICATION="GrowlNotification"
+GROWL_SHUTDOWN="GrowlShutdown"
+GROWL_PING="Honey, Mind Taking Out The Trash"
+GROWL_PONG="What Do You Want From Me, Woman"
+GROWL_IS_READY="Lend Me Some Sugar; I Am Your Neighbor!"
+
+    
+growlPriority = {"Very Low":-2,"Moderate":-1,"Normal":0,"High":1,"Emergency":2}
+
+class netgrowl:
+    """Builds a Growl Network Registration packet.
+       Defaults to emulating the command-line growlnotify utility."""
+
+    __notAllowed__ = [GROWL_APP_ICON, GROWL_NOTIFICATION_ICON, GROWL_NOTIFICATION_APP_ICON]
+
+    def __init__(self, hostname, password ):
+        self.hostname = hostname
+        self.password = password
+        self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
+
+    def send(self, data):
+        self.socket.sendto(data, (self.hostname, GROWL_UDP_PORT))
+        
+    def PostNotification(self, userInfo):
+        if userInfo.has_key(GROWL_NOTIFICATION_PRIORITY):
+            priority = userInfo[GROWL_NOTIFICATION_PRIORITY]
+        else:
+            priority = 0
+        if userInfo.has_key(GROWL_NOTIFICATION_STICKY):
+            sticky = userInfo[GROWL_NOTIFICATION_STICKY]
+        else:
+            sticky = False
+        data = self.encodeNotify(userInfo[GROWL_APP_NAME],
+                                 userInfo[GROWL_NOTIFICATION_NAME],
+                                 userInfo[GROWL_NOTIFICATION_TITLE],
+                                 userInfo[GROWL_NOTIFICATION_DESCRIPTION],
+                                 priority,
+                                 sticky)
+        return self.send(data)
+
+    def PostRegistration(self, userInfo):
+        data = self.encodeRegistration(userInfo[GROWL_APP_NAME],
+                                       userInfo[GROWL_NOTIFICATIONS_ALL],
+                                       userInfo[GROWL_NOTIFICATIONS_DEFAULT])
+        return self.send(data)
+
+    def encodeRegistration(self, application, notifications, defaultNotifications):
+        data = struct.pack("!BBH",
+                           GROWL_PROTOCOL_VERSION,
+                           GROWL_TYPE_REGISTRATION,
+                           len(application) )
+        data += struct.pack("BB",
+                            len(notifications),
+                            len(defaultNotifications) )
+        data += application
+        for i in notifications:
+            encoded = i.encode("utf-8")
+            data += struct.pack("!H", len(encoded))
+            data += encoded
+        for i in defaultNotifications:
+            data += struct.pack("B", i)
+        return self.encodePassword(data)
+
+    def encodeNotify(self, application, notification, title, description,
+                     priority = 0, sticky = False):
+
+        application  = application.encode("utf-8")
+        notification = notification.encode("utf-8")
+        title        = title.encode("utf-8")
+        description  = description.encode("utf-8")
+        flags = (priority & 0x07) * 2
+        if priority < 0: 
+            flags |= 0x08
+        if sticky: 
+            flags = flags | 0x0001
+        data = struct.pack("!BBHHHHH",
+                           GROWL_PROTOCOL_VERSION,
+                           GROWL_TYPE_NOTIFICATION,
+                           flags,
+                           len(notification),
+                           len(title),
+                           len(description),
+                           len(application) )
+        data += notification
+        data += title
+        data += description
+        data += application
+        return self.encodePassword(data)
+
+    def encodePassword(self, data):
+        checksum = md5.new()
+        checksum.update(data)
+        if self.password:
+           checksum.update(self.password)
+        data += checksum.digest()
+        return data
+
+class _ImageHook(type):
+    def __getattribute__(self, attr):
+        global Image
+        if Image is self:
+            from _growlImage import Image
+
+        return getattr(Image, attr)
+
+class Image(object):
+    __metaclass__ = _ImageHook
+
+class _RawImage(object):
+    def __init__(self, data):  self.rawImageData = data
+
+class GrowlNotifier(object):
+    """
+    A class that abstracts the process of registering and posting
+    notifications to the Growl daemon.
+
+    You can either pass `applicationName', `notifications',
+    `defaultNotifications' and `applicationIcon' to the constructor
+    or you may define them as class-level variables in a sub-class.
+
+    `defaultNotifications' is optional, and defaults to the value of
+    `notifications'.  `applicationIcon' is also optional but defaults
+    to a pointless icon so is better to be specified.
+    """
+
+    applicationName = 'GrowlNotifier'
+    notifications = []
+    defaultNotifications = []
+    applicationIcon = None
+    _notifyMethod = _growl
+
+    def __init__(self, applicationName=None, notifications=None, defaultNotifications=None, applicationIcon=None, hostname=None, password=None):
+        if applicationName:
+            self.applicationName = applicationName
+        assert(self.applicationName, 'An application name is required.')
+
+        if notifications:
+            self.notifications = list(notifications)
+        assert(self.notifications, 'A sequence of one or more notification names is required.')
+
+        if defaultNotifications is not None:
+            self.defaultNotifications = list(defaultNotifications)
+        elif not self.defaultNotifications:
+            self.defaultNotifications = list(self.notifications)
+
+        if applicationIcon is not None:
+            self.applicationIcon = self._checkIcon(applicationIcon)
+        elif self.applicationIcon is not None:
+            self.applicationIcon = self._checkIcon(self.applicationIcon)
+
+        if hostname is not None and password is not None:
+            self._notifyMethod = netgrowl(hostname, password)
+        elif hostname is not None or password is not None:
+            raise KeyError, "Hostname and Password are both required for a network notification"
+
+    def _checkIcon(self, data):
+        if isinstance(data, str):
+            return _RawImage(data)
+        else:
+            return data
+
+    def register(self):
+        if self.applicationIcon is not None:
+            self.applicationIcon = self._checkIcon(self.applicationIcon)
+
+        regInfo = {GROWL_APP_NAME: self.applicationName,
+                   GROWL_NOTIFICATIONS_ALL: self.notifications,
+                   GROWL_NOTIFICATIONS_DEFAULT: self.defaultNotifications,
+                   GROWL_APP_ICON:self.applicationIcon,
+                  }
+        self._notifyMethod.PostRegistration(regInfo)
+
+    def notify(self, noteType, title, description, icon=None, sticky=False, priority=None):
+        assert noteType in self.notifications
+        notifyInfo = {GROWL_NOTIFICATION_NAME: noteType,
+                      GROWL_APP_NAME: self.applicationName,
+                      GROWL_NOTIFICATION_TITLE: title,
+                      GROWL_NOTIFICATION_DESCRIPTION: description,
+                     }
+        if sticky:
+            notifyInfo[GROWL_NOTIFICATION_STICKY] = 1
+
+        if priority is not None:
+            notifyInfo[GROWL_NOTIFICATION_PRIORITY] = priority
+
+        if icon:
+            notifyInfo[GROWL_NOTIFICATION_ICON] = self._checkIcon(icon)
+
+        self._notifyMethod.PostNotification(notifyInfo)

growl-python/growlImage.m

+/*
+ * Copyright 2004 Mark Rowe <bdash@users.sourceforge.net>
+ * Released under the BSD license.
+ */
+
+#include "Python.h"
+#import <Cocoa/Cocoa.h>
+
+typedef struct
+{
+  PyObject_HEAD
+  NSImage *theImage;
+} ImageObject;
+
+
+static PyTypeObject ImageObject_Type;
+
+#define ImageObject_Check(v)  ((v)->ob_type == &ImageObject_Type)
+
+static ImageObject *
+newImageObject(NSImage *img)
+{
+    ImageObject *self;
+    if (! img)
+    {
+        PyErr_SetString(PyExc_TypeError, "Invalid image.");
+        return NULL;
+    }
+        
+    self = PyObject_New(ImageObject, &ImageObject_Type);
+    if (! self)
+        return NULL;
+    
+    self->theImage = [img retain];
+    return self;
+}
+
+static void
+ImageObject_dealloc(ImageObject *self)
+{
+    PyObject_Del(self);
+}
+
+static PyObject *
+ImageObject_getAttr(PyObject *self, PyObject *attr)
+{
+    char *theAttr = PyString_AsString(attr);
+    NSAutoreleasePool *pool = nil;
+    
+    if (strcmp(theAttr, "rawImageData") == 0)
+    {
+        pool = [[NSAutoreleasePool alloc] init];
+        NSData *imageData = [((ImageObject *) self)->theImage TIFFRepresentation];
+        PyObject *pyImageData = PyString_FromStringAndSize([imageData bytes], [imageData length]);
+        [pool release];
+        return pyImageData;
+    }
+    else
+        return PyObject_GenericGetAttr(self, attr);
+}
+
+
+static PyObject *
+ImageObject_imageFromPath(PyTypeObject *cls, PyObject *args)
+{
+    ImageObject *self;
+    char *fileName_ = NULL;
+    NSString *fileName = nil;
+    NSImage *theImage = nil;
+    NSAutoreleasePool *pool = nil;
+    
+    if (! PyArg_ParseTuple(args, "et:imageFromPath",
+                           Py_FileSystemDefaultEncoding, &fileName_))
+        return NULL;
+        
+    pool = [[NSAutoreleasePool alloc] init];
+    
+    fileName = [NSString stringWithUTF8String:fileName_];
+    theImage = [[[NSImage alloc] initWithContentsOfFile:fileName] autorelease];
+    self = newImageObject(theImage);
+    
+    [pool release];
+    return (PyObject *) self;
+}
+
+static PyObject *
+ImageObject_imageWithData(PyTypeObject *cls, PyObject *args)
+{
+    ImageObject *self;
+    char *imageData = NULL;
+    int imageDataSize = 0;
+    NSImage *theImage = nil;
+    NSAutoreleasePool *pool = nil;
+    
+    if (! PyArg_ParseTuple(args, "s#:imageWithData",
+                           &imageData, &imageDataSize))
+        return NULL;
+        
+    pool = [[NSAutoreleasePool alloc] init];
+    
+
+    theImage = [[[NSImage alloc] initWithData:[NSData dataWithBytes:imageData
+                                                             length:imageDataSize]] autorelease];
+    self = newImageObject(theImage);
+    
+    [pool release];
+    return (PyObject *) self;
+}
+
+static PyObject *
+ImageObject_imageWithIconForFile(PyTypeObject *cls, PyObject *args)
+{
+    ImageObject *self;
+    char *fileName_ = NULL;
+    NSString *fileName = nil;
+    NSImage *theImage = nil;
+    NSAutoreleasePool *pool = nil;
+    
+    if (! PyArg_ParseTuple(args, "et:imageWithIconForFile",
+                           Py_FileSystemDefaultEncoding, &fileName_))
+        return NULL;
+        
+    pool = [[NSAutoreleasePool alloc] init];
+    
+    fileName = [NSString stringWithUTF8String:fileName_];
+    theImage = [[NSWorkspace sharedWorkspace] iconForFile:fileName];
+    self = newImageObject(theImage);
+    
+    [pool release];
+    return (PyObject *) self;
+}
+
+static PyObject *
+ImageObject_imageWithIconForFileType(PyTypeObject *cls, PyObject *args)
+{
+    ImageObject *self;
+    char *fileType = NULL;
+    NSImage *theImage = nil;
+    NSAutoreleasePool *pool = nil;
+    
+    if (! PyArg_ParseTuple(args, "s:imageWithIconForFileType",
+                           &fileType))
+        return NULL;
+        
+    pool = [[NSAutoreleasePool alloc] init];
+    
+    theImage = [[NSWorkspace sharedWorkspace] iconForFileType:[NSString stringWithUTF8String:fileType]];
+    self = newImageObject(theImage);
+    
+    [pool release];
+    return (PyObject *) self;
+}
+
+static PyObject *
+ImageObject_imageWithIconForCurrentApplication(PyTypeObject *cls, PyObject *args)
+{
+    ImageObject *self;
+    NSAutoreleasePool *pool = nil;
+    
+    if (! PyArg_ParseTuple(args, ":imageWithIconForCurrentApplication"))
+        return NULL;
+        
+    pool = [[NSAutoreleasePool alloc] init];
+    
+    self = newImageObject([NSApp applicationIconImage]);
+    
+    [pool release];
+    return (PyObject *) self;
+}
+
+static PyObject *
+ImageObject_imageWithIconForApplication(PyTypeObject *cls, PyObject *args)
+{
+    ImageObject *self;
+    char *appName_ = NULL;
+    NSString *appName = nil;
+    NSString *appPath = nil;
+    NSImage *theImage = nil;
+    NSAutoreleasePool *pool = nil;
+    
+    if (! PyArg_ParseTuple(args, "et:imageWithIconForApplication",
+                           Py_FileSystemDefaultEncoding, &appName_))
+        return NULL;
+        
+    pool = [[NSAutoreleasePool alloc] init];
+    
+    appName = [NSString stringWithUTF8String:appName_];
+    appPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:appName];
+    if (! appPath)
+    {
+        PyErr_Format(PyExc_RuntimeError, "Application named '%s' not found", appName_);
+        self = NULL;
+        goto done;
+    }
+    theImage = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
+    self = newImageObject(theImage);
+
+done:
+    [pool release];
+    return (PyObject *) self;
+}
+
+
+static PyMethodDef ImageObject_methods[] = {
+    {"imageFromPath", (PyCFunction)ImageObject_imageFromPath, METH_VARARGS | METH_CLASS},
+    {"imageWithData", (PyCFunction)ImageObject_imageWithData, METH_VARARGS | METH_CLASS},
+    {"imageWithIconForFile", (PyCFunction)ImageObject_imageWithIconForFile, METH_VARARGS | METH_CLASS},
+    {"imageWithIconForFileType", (PyCFunction)ImageObject_imageWithIconForFileType, METH_VARARGS | METH_CLASS},
+    {"imageWithIconForCurrentApplication", (PyCFunction)ImageObject_imageWithIconForCurrentApplication, METH_VARARGS | METH_CLASS},
+    {"imageWithIconForApplication", (PyCFunction)ImageObject_imageWithIconForApplication, METH_VARARGS | METH_CLASS},
+    {NULL, NULL} /* sentinel */
+};
+
+static PyTypeObject ImageObject_Type = {
+        PyObject_HEAD_INIT(NULL)
+        0,                      /*ob_size*/
+        "_growlImage.Image",    /*tp_name*/
+        sizeof(ImageObject),    /*tp_basicsize*/
+        0,                      /*tp_itemsize*/
+        /* methods */
+        (destructor)ImageObject_dealloc, /*tp_dealloc*/
+        0,                      /*tp_print*/
+        0,                      /*tp_getattr*/
+        0,                      /*tp_setattr*/
+        0,                      /*tp_compare*/
+        0,                      /*tp_repr*/
+        0,                      /*tp_as_number*/
+        0,                      /*tp_as_sequence*/
+        0,                      /*tp_as_mapping*/
+        0,                      /*tp_hash*/
+        0,                      /*tp_call*/
+        0,                      /*tp_str*/
+        ImageObject_getAttr,    /*tp_getattro*/
+        0,                      /*tp_setattro*/
+        0,                      /*tp_as_buffer*/
+        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_CLASS,     /*tp_flags*/
+        0,                      /*tp_doc*/
+        0,                      /*tp_traverse*/
+        0,                      /*tp_clear*/
+        0,                      /*tp_richcompare*/
+        0,                      /*tp_weaklistoffset*/
+        0,                      /*tp_iter*/
+        0,                      /*tp_iternext*/
+        ImageObject_methods,    /*tp_methods*/
+        0,                      /*tp_members*/
+        0,                      /*tp_getset*/
+        0,                      /*tp_base*/
+        0,                      /*tp_dict*/
+        0,                      /*tp_descr_get*/
+        0,                      /*tp_descr_set*/
+        0,                      /*tp_dictoffset*/
+        0,                      /*tp_init*/
+        PyType_GenericAlloc,    /*tp_alloc*/
+        0,                      /*tp_new*/
+        0,                      /*tp_free*/
+        0,                      /*tp_is_gc*/
+};
+
+static PyMethodDef _growlImage_methods[] = {
+    {NULL, NULL}
+};
+
+PyMODINIT_FUNC
+init_growlImage(void)
+{
+    PyObject *m;
+    
+    if (PyType_Ready(&ImageObject_Type) < 0)
+        return;
+    
+    m = Py_InitModule("_growlImage", _growlImage_methods);
+    
+    PyModule_AddObject(m, "Image", (PyObject *)&ImageObject_Type);
+}

growl-python/libgrowl.c

+/*
+ * Copyright 2004-2005 The Growl Project.
+ * Created by Jeremy Rossi <jeremy@jeremyrossi.com>
+ * Released under the BSD license.
+ */
+
+#include <Python.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+static PyObject * growl_PostDictionary(CFStringRef name, PyObject *self, PyObject *args) {
+	int i, j;
+
+	PyObject *inputDict;
+	PyObject *pKeys = NULL;
+	PyObject *pKey, *pValue;
+
+	CFMutableDictionaryRef note = CFDictionaryCreateMutable(kCFAllocatorDefault,
+															/*capacity*/ 0,
+															&kCFTypeDictionaryKeyCallBacks,
+															&kCFTypeDictionaryValueCallBacks);
+
+	if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &inputDict))
+		goto error;
+
+	pKeys = PyDict_Keys(inputDict);
+	for (i = 0; i < PyList_Size(pKeys); ++i) {
+		CFStringRef convertedKey;
+
+		/* Converting the PyDict key to NSString and used for key in note */
+		pKey = PyList_GetItem(pKeys, i);
+		if (!pKey)
+			// Exception already set
+			goto error;
+		pValue = PyDict_GetItem(inputDict, pKey);
+		if (!pValue) {
+			// XXX Neeed a real Error message here.
+			PyErr_SetString(PyExc_TypeError," ");
+			goto error;
+		}
+		if (PyUnicode_Check(pKey)) {
+			convertedKey = CFStringCreateWithBytes(kCFAllocatorDefault,
+												   (const UInt8 *)PyUnicode_AS_DATA(pKey),
+												   PyUnicode_GET_DATA_SIZE(pKey),
+												   kCFStringEncodingUnicode,
+												   false);
+		} else if (PyString_Check(pKey)) {
+			convertedKey = CFStringCreateWithCString(kCFAllocatorDefault,
+													 PyString_AsString(pKey),
+													 kCFStringEncodingUTF8);
+		} else {
+			PyErr_SetString(PyExc_TypeError,"The Dict keys must be strings/unicode");
+			goto error;
+		}
+
+		/* Converting the PyDict value to NSString or NSData based on class  */
+		if (PyString_Check(pValue)) {
+			CFStringRef convertedValue = CFStringCreateWithCString(kCFAllocatorDefault,
+																   PyString_AS_STRING(pValue),
+																   kCFStringEncodingUTF8);
+			CFDictionarySetValue(note, convertedKey, convertedValue);
+			CFRelease(convertedValue);
+		} else if (PyUnicode_Check(pValue)) {
+			CFStringRef convertedValue = CFStringCreateWithBytes(kCFAllocatorDefault,
+																 (const UInt8 *)PyUnicode_AS_DATA(pValue),
+																 PyUnicode_GET_DATA_SIZE(pValue),
+																 kCFStringEncodingUnicode,
+																 false);
+			CFDictionarySetValue(note, convertedKey, convertedValue);
+			CFRelease(convertedValue);
+		} else if (PyInt_Check(pValue)) {
+			long v = PyInt_AS_LONG(pValue);
+			CFNumberRef convertedValue = CFNumberCreate(kCFAllocatorDefault,
+														kCFNumberLongType,
+														&v);
+			CFDictionarySetValue(note, convertedKey, convertedValue);
+			CFRelease(convertedValue);
+		} else if (pValue == Py_None) {
+			CFDataRef convertedValue = CFDataCreate(kCFAllocatorDefault, NULL, 0);
+			CFDictionarySetValue(note, convertedKey, convertedValue);
+			CFRelease(convertedValue);
+		} else if (PyList_Check(pValue)) {
+			int size = PyList_Size(pValue);
+			CFMutableArrayRef listHolder = CFArrayCreateMutable(kCFAllocatorDefault,
+																size,
+																&kCFTypeArrayCallBacks);
+			for (j = 0; j < size; ++j) {
+				PyObject *lValue = PyList_GetItem(pValue, j);
+				if (PyString_Check(lValue)) {
+					CFStringRef convertedValue = CFStringCreateWithCString(kCFAllocatorDefault,
+																		   PyString_AS_STRING(lValue),
+																		   kCFStringEncodingUTF8);
+					CFArrayAppendValue(listHolder, convertedValue);
+					CFRelease(convertedValue);
+				} else if (PyUnicode_Check(lValue)) {
+					CFStringRef convertedValue = CFStringCreateWithBytes(kCFAllocatorDefault,
+																		 (const UInt8 *)PyUnicode_AS_DATA(lValue),
+																		 PyUnicode_GET_DATA_SIZE(lValue),
+																		 kCFStringEncodingUnicode,
+																		 false);
+					CFArrayAppendValue(listHolder, convertedValue);
+					CFRelease(convertedValue);
+				} else {
+					CFRelease(convertedKey);
+					PyErr_SetString(PyExc_TypeError,"The lists must only contain strings");
+					goto error;
+				}
+			}
+			CFDictionarySetValue(note, convertedKey, listHolder);
+			CFRelease(listHolder);
+		} else if (PyObject_HasAttrString(pValue, "rawImageData")) {
+			PyObject *lValue = PyObject_GetAttrString(pValue, "rawImageData");
+			if (!lValue) {
+				goto error;
+			} else if (PyString_Check(lValue)) {
+				CFDataRef convertedValue = CFDataCreate(kCFAllocatorDefault,
+														(const UInt8 *)PyString_AsString(lValue),
+														PyString_Size(lValue));
+				CFDictionarySetValue(note, convertedKey, convertedValue);
+				CFRelease(convertedValue);
+			} else {
+				CFRelease(convertedKey);
+				PyErr_SetString(PyExc_TypeError, "Icon with rawImageData attribute present must ensure it is a string.");
+				goto error;
+			}
+		} else {
+			CFRelease(convertedKey);
+			PyErr_SetString(PyExc_TypeError, "Value is not of Str/List");
+			goto error;
+		}
+		CFRelease(convertedKey);
+	}
+
+	Py_BEGIN_ALLOW_THREADS
+	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
+										 /*name*/ name,
+										 /*object*/ NULL,
+										 /*userInfo*/ note,
+										 /*deliverImmediately*/ false);
+	CFRelease(note);
+	Py_END_ALLOW_THREADS
+
+	Py_DECREF(pKeys);
+
+	Py_INCREF(Py_None);
+	return Py_None;
+
+error:
+	CFRelease(note);
+
+	Py_XDECREF(pKeys);
+
+	return NULL;
+}
+
+static PyObject * growl_PostRegistration(PyObject *self, PyObject *args) {
+	return growl_PostDictionary(CFSTR("GrowlApplicationRegistrationNotification"), self, args);
+}
+
+static PyObject * growl_PostNotification(PyObject *self, PyObject *args) {
+	return growl_PostDictionary(CFSTR("GrowlNotification"), self, args);
+}
+
+static PyMethodDef GrowlMethods[] = {
+	{"PostNotification", growl_PostNotification, METH_VARARGS, "Send a notification to GrowlHelperApp"},
+	{"PostRegistration", growl_PostRegistration, METH_VARARGS, "Send a registration to GrowlHelperApp"},
+	{NULL, NULL, 0, NULL}		/* Sentinel */
+};
+
+
+PyMODINIT_FUNC init_growl(void) {
+	Py_InitModule("_growl", GrowlMethods);
+}

growl-python/setup.py

+#!/usr/bin/python
+from setuptools import setup, Extension
+import sys
+
+_growl = Extension('_growl',
+                    extra_link_args = ["-framework","CoreFoundation"],
+                    sources = ['libgrowl.c'])
+_growlImage = Extension('_growlImage',
+                        extra_link_args = ["-framework","Cocoa"],
+                        sources = ['growlImage.m'])
+
+if sys.platform.startswith("darwin"):
+    modules = [_growl, _growlImage]
+else:
+    modules = []
+
+setup(name="py-Growl",
+      version="0.0.7",
+      description="Python bindings for posting notifications to the Growl daemon",
+      author="Mark Rowe",
+      author_email="bdash@users.sourceforge.net",
+      url="http://growl.info",
+      py_modules=["Growl"],
+      ext_modules = modules )
+

growl-python/trunk/Growl.py

-"""
-A Python module that enables posting notifications to the Growl daemon.
-See <http://growl.info/> for more information.
-"""
-__version__ = "0.7" 
-__author__ = "Mark Rowe <bdash@users.sourceforge.net>"
-__copyright__ = "(C) 2003 Mark Rowe <bdash@users.sourceforge.net>. Released under the BSD license."
-__contributors__ = ["Ingmar J Stein (Growl Team)", 
-                    "Rui Carmo (http://the.taoofmac.com)",
-                    "Jeremy Rossi <jeremy@jeremyrossi.com>",
-                    "Peter Hosey <http://boredzo.org/> (Growl Team)",
-                   ]
-
-import _growl
-import types
-import struct
-import md5
-import socket
-
-GROWL_UDP_PORT=9887
-GROWL_PROTOCOL_VERSION=1
-GROWL_TYPE_REGISTRATION=0
-GROWL_TYPE_NOTIFICATION=1
-
-GROWL_APP_NAME="ApplicationName"
-GROWL_APP_ICON="ApplicationIcon"
-GROWL_NOTIFICATIONS_DEFAULT="DefaultNotifications"
-GROWL_NOTIFICATIONS_ALL="AllNotifications"
-GROWL_NOTIFICATIONS_USER_SET="AllowedUserNotifications"
-
-GROWL_NOTIFICATION_NAME="NotificationName"
-GROWL_NOTIFICATION_TITLE="NotificationTitle"
-GROWL_NOTIFICATION_DESCRIPTION="NotificationDescription"
-GROWL_NOTIFICATION_ICON="NotificationIcon"
-GROWL_NOTIFICATION_APP_ICON="NotificationAppIcon"
-GROWL_NOTIFICATION_PRIORITY="NotificationPriority"
-        
-GROWL_NOTIFICATION_STICKY="NotificationSticky"
-
-GROWL_APP_REGISTRATION="GrowlApplicationRegistrationNotification"
-GROWL_APP_REGISTRATION_CONF="GrowlApplicationRegistrationConfirmationNotification"
-GROWL_NOTIFICATION="GrowlNotification"
-GROWL_SHUTDOWN="GrowlShutdown"
-GROWL_PING="Honey, Mind Taking Out The Trash"
-GROWL_PONG="What Do You Want From Me, Woman"
-GROWL_IS_READY="Lend Me Some Sugar; I Am Your Neighbor!"
-
-    
-growlPriority = {"Very Low":-2,"Moderate":-1,"Normal":0,"High":1,"Emergency":2}
-
-class netgrowl:
-    """Builds a Growl Network Registration packet.
-       Defaults to emulating the command-line growlnotify utility."""
-
-    __notAllowed__ = [GROWL_APP_ICON, GROWL_NOTIFICATION_ICON, GROWL_NOTIFICATION_APP_ICON]
-
-    def __init__(self, hostname, password ):
-        self.hostname = hostname
-        self.password = password
-        self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
-
-    def send(self, data):
-        self.socket.sendto(data, (self.hostname, GROWL_UDP_PORT))
-        
-    def PostNotification(self, userInfo):
-        if userInfo.has_key(GROWL_NOTIFICATION_PRIORITY):
-            priority = userInfo[GROWL_NOTIFICATION_PRIORITY]
-        else:
-            priority = 0
-        if userInfo.has_key(GROWL_NOTIFICATION_STICKY):
-            sticky = userInfo[GROWL_NOTIFICATION_STICKY]
-        else:
-            sticky = False
-        data = self.encodeNotify(userInfo[GROWL_APP_NAME],
-                                 userInfo[GROWL_NOTIFICATION_NAME],
-                                 userInfo[GROWL_NOTIFICATION_TITLE],
-                                 userInfo[GROWL_NOTIFICATION_DESCRIPTION],
-                                 priority,
-                                 sticky)
-        return self.send(data)
-
-    def PostRegistration(self, userInfo):
-        data = self.encodeRegistration(userInfo[GROWL_APP_NAME],
-                                       userInfo[GROWL_NOTIFICATIONS_ALL],
-                                       userInfo[GROWL_NOTIFICATIONS_DEFAULT])
-        return self.send(data)
-
-    def encodeRegistration(self, application, notifications, defaultNotifications):
-        data = struct.pack("!BBH",
-                           GROWL_PROTOCOL_VERSION,
-                           GROWL_TYPE_REGISTRATION,
-                           len(application) )
-        data += struct.pack("BB",
-                            len(notifications),
-                            len(defaultNotifications) )
-        data += application
-        for i in notifications:
-            encoded = i.encode("utf-8")
-            data += struct.pack("!H", len(encoded))
-            data += encoded
-        for i in defaultNotifications:
-            data += struct.pack("B", i)
-        return self.encodePassword(data)
-
-    def encodeNotify(self, application, notification, title, description,
-                     priority = 0, sticky = False):
-
-        application  = application.encode("utf-8")
-        notification = notification.encode("utf-8")
-        title        = title.encode("utf-8")
-        description  = description.encode("utf-8")
-        flags = (priority & 0x07) * 2
-        if priority < 0: 
-            flags |= 0x08
-        if sticky: 
-            flags = flags | 0x0001
-        data = struct.pack("!BBHHHHH",
-                           GROWL_PROTOCOL_VERSION,
-                           GROWL_TYPE_NOTIFICATION,
-                           flags,
-                           len(notification),
-                           len(title),
-                           len(description),
-                           len(application) )
-        data += notification
-        data += title
-        data += description
-        data += application
-        return self.encodePassword(data)
-
-    def encodePassword(self, data):
-        checksum = md5.new()
-        checksum.update(data)
-        if self.password:
-           checksum.update(self.password)
-        data += checksum.digest()
-        return data
-
-class _ImageHook(type):
-    def __getattribute__(self, attr):
-        global Image
-        if Image is self:
-            from _growlImage import Image
-
-        return getattr(Image, attr)
-
-class Image(object):
-    __metaclass__ = _ImageHook
-
-class _RawImage(object):
-    def __init__(self, data):  self.rawImageData = data
-
-class GrowlNotifier(object):
-    """
-    A class that abstracts the process of registering and posting
-    notifications to the Growl daemon.
-
-    You can either pass `applicationName', `notifications',
-    `defaultNotifications' and `applicationIcon' to the constructor
-    or you may define them as class-level variables in a sub-class.
-
-    `defaultNotifications' is optional, and defaults to the value of
-    `notifications'.  `applicationIcon' is also optional but defaults
-    to a pointless icon so is better to be specified.
-    """
-
-    applicationName = 'GrowlNotifier'
-    notifications = []
-    defaultNotifications = []
-    applicationIcon = None
-    _notifyMethod = _growl
-
-    def __init__(self, applicationName=None, notifications=None, defaultNotifications=None, applicationIcon=None, hostname=None, password=None):
-        if applicationName:
-            self.applicationName = applicationName
-        assert(self.applicationName, 'An application name is required.')
-
-        if notifications:
-            self.notifications = list(notifications)
-        assert(self.notifications, 'A sequence of one or more notification names is required.')
-
-        if defaultNotifications is not None:
-            self.defaultNotifications = list(defaultNotifications)
-        elif not self.defaultNotifications:
-            self.defaultNotifications = list(self.notifications)
-
-        if applicationIcon is not None:
-            self.applicationIcon = self._checkIcon(applicationIcon)
-        elif self.applicationIcon is not None:
-            self.applicationIcon = self._checkIcon(self.applicationIcon)
-
-        if hostname is not None and password is not None:
-            self._notifyMethod = netgrowl(hostname, password)
-        elif hostname is not None or password is not None:
-            raise KeyError, "Hostname and Password are both required for a network notification"
-
-    def _checkIcon(self, data):
-        if isinstance(data, str):
-            return _RawImage(data)
-        else:
-            return data
-
-    def register(self):
-        if self.applicationIcon is not None:
-            self.applicationIcon = self._checkIcon(self.applicationIcon)
-
-        regInfo = {GROWL_APP_NAME: self.applicationName,
-                   GROWL_NOTIFICATIONS_ALL: self.notifications,
-                   GROWL_NOTIFICATIONS_DEFAULT: self.defaultNotifications,
-                   GROWL_APP_ICON:self.applicationIcon,
-                  }
-        self._notifyMethod.PostRegistration(regInfo)
-
-    def notify(self, noteType, title, description, icon=None, sticky=False, priority=None):
-        assert noteType in self.notifications
-        notifyInfo = {GROWL_NOTIFICATION_NAME: noteType,
-                      GROWL_APP_NAME: self.applicationName,
-                      GROWL_NOTIFICATION_TITLE: title,
-                      GROWL_NOTIFICATION_DESCRIPTION: description,
-                     }
-        if sticky:
-            notifyInfo[GROWL_NOTIFICATION_STICKY] = 1
-
-        if priority is not None:
-            notifyInfo[GROWL_NOTIFICATION_PRIORITY] = priority
-
-        if icon:
-            notifyInfo[GROWL_NOTIFICATION_ICON] = self._checkIcon(icon)
-
-        self._notifyMethod.PostNotification(notifyInfo)

growl-python/trunk/growlImage.m

-/*
- * Copyright 2004 Mark Rowe <bdash@users.sourceforge.net>
- * Released under the BSD license.
- */
-
-#include "Python.h"
-#import <Cocoa/Cocoa.h>
-
-typedef struct
-{
-  PyObject_HEAD
-  NSImage *theImage;
-} ImageObject;
-
-
-static PyTypeObject ImageObject_Type;
-
-#define ImageObject_Check(v)  ((v)->ob_type == &ImageObject_Type)
-
-static ImageObject *
-newImageObject(NSImage *img)
-{
-    ImageObject *self;
-    if (! img)
-    {
-        PyErr_SetString(PyExc_TypeError, "Invalid image.");
-        return NULL;
-    }
-        
-    self = PyObject_New(ImageObject, &ImageObject_Type);
-    if (! self)
-        return NULL;
-    
-    self->theImage = [img retain];
-    return self;
-}
-
-static void
-ImageObject_dealloc(ImageObject *self)
-{
-    PyObject_Del(self);
-}
-
-static PyObject *
-ImageObject_getAttr(PyObject *self, PyObject *attr)
-{
-    char *theAttr = PyString_AsString(attr);
-    NSAutoreleasePool *pool = nil;
-    
-    if (strcmp(theAttr, "rawImageData") == 0)
-    {
-        pool = [[NSAutoreleasePool alloc] init];
-        NSData *imageData = [((ImageObject *) self)->theImage TIFFRepresentation];
-        PyObject *pyImageData = PyString_FromStringAndSize([imageData bytes], [imageData length]);
-        [pool release];
-        return pyImageData;
-    }
-    else
-        return PyObject_GenericGetAttr(self, attr);
-}
-
-
-static PyObject *
-ImageObject_imageFromPath(PyTypeObject *cls, PyObject *args)
-{
-    ImageObject *self;
-    char *fileName_ = NULL;
-    NSString *fileName = nil;
-    NSImage *theImage = nil;
-    NSAutoreleasePool *pool = nil;
-    
-    if (! PyArg_ParseTuple(args, "et:imageFromPath",
-                           Py_FileSystemDefaultEncoding, &fileName_))
-        return NULL;
-        
-    pool = [[NSAutoreleasePool alloc] init];
-    
-    fileName = [NSString stringWithUTF8String:fileName_];
-    theImage = [[[NSImage alloc] initWithContentsOfFile:fileName] autorelease];
-    self = newImageObject(theImage);
-    
-    [pool release];
-    return (PyObject *) self;
-}
-
-static PyObject *
-ImageObject_imageWithData(PyTypeObject *cls, PyObject *args)
-{
-    ImageObject *self;
-    char *imageData = NULL;
-    int imageDataSize = 0;
-    NSImage *theImage = nil;
-    NSAutoreleasePool *pool = nil;
-    
-    if (! PyArg_ParseTuple(args, "s#:imageWithData",
-                           &imageData, &imageDataSize))
-        return NULL;
-        
-    pool = [[NSAutoreleasePool alloc] init];
-    
-
-    theImage = [[[NSImage alloc] initWithData:[NSData dataWithBytes:imageData
-                                                             length:imageDataSize]] autorelease];
-    self = newImageObject(theImage);
-    
-    [pool release];
-    return (PyObject *) self;
-}
-
-static PyObject *
-ImageObject_imageWithIconForFile(PyTypeObject *cls, PyObject *args)
-{
-    ImageObject *self;
-    char *fileName_ = NULL;
-    NSString *fileName = nil;
-    NSImage *theImage = nil;
-    NSAutoreleasePool *pool = nil;
-    
-    if (! PyArg_ParseTuple(args, "et:imageWithIconForFile",
-                           Py_FileSystemDefaultEncoding, &fileName_))
-        return NULL;
-        
-    pool = [[NSAutoreleasePool alloc] init];
-    
-    fileName = [NSString stringWithUTF8String:fileName_];
-    theImage = [[NSWorkspace sharedWorkspace] iconForFile:fileName];
-    self = newImageObject(theImage);
-    
-    [pool release];
-    return (PyObject *) self;
-}
-
-static PyObject *
-ImageObject_imageWithIconForFileType(PyTypeObject *cls, PyObject *args)
-{
-    ImageObject *self;
-    char *fileType = NULL;
-    NSImage *theImage = nil;
-    NSAutoreleasePool *pool = nil;
-    
-    if (! PyArg_ParseTuple(args, "s:imageWithIconForFileType",
-                           &fileType))
-        return NULL;
-        
-    pool = [[NSAutoreleasePool alloc] init];
-    
-    theImage = [[NSWorkspace sharedWorkspace] iconForFileType:[NSString stringWithUTF8String:fileType]];
-    self = newImageObject(theImage);
-    
-    [pool release];
-    return (PyObject *) self;
-}
-
-static PyObject *
-ImageObject_imageWithIconForCurrentApplication(PyTypeObject *cls, PyObject *args)
-{
-    ImageObject *self;
-    NSAutoreleasePool *pool = nil;
-    
-    if (! PyArg_ParseTuple(args, ":imageWithIconForCurrentApplication"))
-        return NULL;
-        
-    pool = [[NSAutoreleasePool alloc] init];
-    
-    self = newImageObject([NSApp applicationIconImage]);
-    
-    [pool release];
-    return (PyObject *) self;
-}
-
-static PyObject *
-ImageObject_imageWithIconForApplication(PyTypeObject *cls, PyObject *args)
-{
-    ImageObject *self;
-    char *appName_ = NULL;
-    NSString *appName = nil;
-    NSString *appPath = nil;
-    NSImage *theImage = nil;
-    NSAutoreleasePool *pool = nil;
-    
-    if (! PyArg_ParseTuple(args, "et:imageWithIconForApplication",
-                           Py_FileSystemDefaultEncoding, &appName_))
-        return NULL;
-        
-    pool = [[NSAutoreleasePool alloc] init];
-    
-    appName = [NSString stringWithUTF8String:appName_];
-    appPath = [[NSWorkspace sharedWorkspace] fullPathForApplication:appName];
-    if (! appPath)
-    {
-        PyErr_Format(PyExc_RuntimeError, "Application named '%s' not found", appName_);
-        self = NULL;
-        goto done;
-    }
-    theImage = [[NSWorkspace sharedWorkspace] iconForFile:appPath];
-    self = newImageObject(theImage);
-
-done:
-    [pool release];
-    return (PyObject *) self;
-}
-
-
-static PyMethodDef ImageObject_methods[] = {
-    {"imageFromPath", (PyCFunction)ImageObject_imageFromPath, METH_VARARGS | METH_CLASS},
-    {"imageWithData", (PyCFunction)ImageObject_imageWithData, METH_VARARGS | METH_CLASS},
-    {"imageWithIconForFile", (PyCFunction)ImageObject_imageWithIconForFile, METH_VARARGS | METH_CLASS},
-    {"imageWithIconForFileType", (PyCFunction)ImageObject_imageWithIconForFileType, METH_VARARGS | METH_CLASS},
-    {"imageWithIconForCurrentApplication", (PyCFunction)ImageObject_imageWithIconForCurrentApplication, METH_VARARGS | METH_CLASS},
-    {"imageWithIconForApplication", (PyCFunction)ImageObject_imageWithIconForApplication, METH_VARARGS | METH_CLASS},
-    {NULL, NULL} /* sentinel */
-};
-
-static PyTypeObject ImageObject_Type = {
-        PyObject_HEAD_INIT(NULL)
-        0,                      /*ob_size*/
-        "_growlImage.Image",    /*tp_name*/
-        sizeof(ImageObject),    /*tp_basicsize*/
-        0,                      /*tp_itemsize*/
-        /* methods */
-        (destructor)ImageObject_dealloc, /*tp_dealloc*/
-        0,                      /*tp_print*/
-        0,                      /*tp_getattr*/
-        0,                      /*tp_setattr*/
-        0,                      /*tp_compare*/
-        0,                      /*tp_repr*/
-        0,                      /*tp_as_number*/
-        0,                      /*tp_as_sequence*/
-        0,                      /*tp_as_mapping*/
-        0,                      /*tp_hash*/
-        0,                      /*tp_call*/
-        0,                      /*tp_str*/
-        ImageObject_getAttr,    /*tp_getattro*/
-        0,                      /*tp_setattro*/
-        0,                      /*tp_as_buffer*/
-        Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_CLASS,     /*tp_flags*/
-        0,                      /*tp_doc*/
-        0,                      /*tp_traverse*/
-        0,                      /*tp_clear*/
-        0,                      /*tp_richcompare*/
-        0,                      /*tp_weaklistoffset*/
-        0,                      /*tp_iter*/
-        0,                      /*tp_iternext*/
-        ImageObject_methods,    /*tp_methods*/
-        0,                      /*tp_members*/
-        0,                      /*tp_getset*/
-        0,                      /*tp_base*/
-        0,                      /*tp_dict*/
-        0,                      /*tp_descr_get*/
-        0,                      /*tp_descr_set*/
-        0,                      /*tp_dictoffset*/
-        0,                      /*tp_init*/
-        PyType_GenericAlloc,    /*tp_alloc*/
-        0,                      /*tp_new*/
-        0,                      /*tp_free*/
-        0,                      /*tp_is_gc*/
-};
-
-static PyMethodDef _growlImage_methods[] = {
-    {NULL, NULL}
-};
-
-PyMODINIT_FUNC
-init_growlImage(void)
-{
-    PyObject *m;
-    
-    if (PyType_Ready(&ImageObject_Type) < 0)
-        return;
-    
-    m = Py_InitModule("_growlImage", _growlImage_methods);
-    
-    PyModule_AddObject(m, "Image", (PyObject *)&ImageObject_Type);
-}

growl-python/trunk/libgrowl.c

-/*
- * Copyright 2004-2005 The Growl Project.
- * Created by Jeremy Rossi <jeremy@jeremyrossi.com>
- * Released under the BSD license.
- */
-
-#include <Python.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-static PyObject * growl_PostDictionary(CFStringRef name, PyObject *self, PyObject *args) {
-	int i, j;
-
-	PyObject *inputDict;
-	PyObject *pKeys = NULL;
-	PyObject *pKey, *pValue;
-
-	CFMutableDictionaryRef note = CFDictionaryCreateMutable(kCFAllocatorDefault,
-															/*capacity*/ 0,
-															&kCFTypeDictionaryKeyCallBacks,
-															&kCFTypeDictionaryValueCallBacks);
-
-	if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, &inputDict))
-		goto error;
-
-	pKeys = PyDict_Keys(inputDict);
-	for (i = 0; i < PyList_Size(pKeys); ++i) {
-		CFStringRef convertedKey;
-
-		/* Converting the PyDict key to NSString and used for key in note */
-		pKey = PyList_GetItem(pKeys, i);
-		if (!pKey)
-			// Exception already set
-			goto error;
-		pValue = PyDict_GetItem(inputDict, pKey);
-		if (!pValue) {
-			// XXX Neeed a real Error message here.
-			PyErr_SetString(PyExc_TypeError," ");
-			goto error;
-		}
-		if (PyUnicode_Check(pKey)) {
-			convertedKey = CFStringCreateWithBytes(kCFAllocatorDefault,
-												   (const UInt8 *)PyUnicode_AS_DATA(pKey),
-												   PyUnicode_GET_DATA_SIZE(pKey),
-												   kCFStringEncodingUnicode,
-												   false);
-		} else if (PyString_Check(pKey)) {
-			convertedKey = CFStringCreateWithCString(kCFAllocatorDefault,
-													 PyString_AsString(pKey),
-													 kCFStringEncodingUTF8);
-		} else {
-			PyErr_SetString(PyExc_TypeError,"The Dict keys must be strings/unicode");
-			goto error;
-		}
-
-		/* Converting the PyDict value to NSString or NSData based on class  */
-		if (PyString_Check(pValue)) {
-			CFStringRef convertedValue = CFStringCreateWithCString(kCFAllocatorDefault,
-																   PyString_AS_STRING(pValue),
-																   kCFStringEncodingUTF8);
-			CFDictionarySetValue(note, convertedKey, convertedValue);
-			CFRelease(convertedValue);
-		} else if (PyUnicode_Check(pValue)) {
-			CFStringRef convertedValue = CFStringCreateWithBytes(kCFAllocatorDefault,
-																 (const UInt8 *)PyUnicode_AS_DATA(pValue),
-																 PyUnicode_GET_DATA_SIZE(pValue),
-																 kCFStringEncodingUnicode,
-																 false);
-			CFDictionarySetValue(note, convertedKey, convertedValue);
-			CFRelease(convertedValue);
-		} else if (PyInt_Check(pValue)) {
-			long v = PyInt_AS_LONG(pValue);
-			CFNumberRef convertedValue = CFNumberCreate(kCFAllocatorDefault,
-														kCFNumberLongType,
-														&v);
-			CFDictionarySetValue(note, convertedKey, convertedValue);
-			CFRelease(convertedValue);
-		} else if (pValue == Py_None) {
-			CFDataRef convertedValue = CFDataCreate(kCFAllocatorDefault, NULL, 0);
-			CFDictionarySetValue(note, convertedKey, convertedValue);
-			CFRelease(convertedValue);
-		} else if (PyList_Check(pValue)) {
-			int size = PyList_Size(pValue);
-			CFMutableArrayRef listHolder = CFArrayCreateMutable(kCFAllocatorDefault,
-																size,
-																&kCFTypeArrayCallBacks);
-			for (j = 0; j < size; ++j) {
-				PyObject *lValue = PyList_GetItem(pValue, j);
-				if (PyString_Check(lValue)) {
-					CFStringRef convertedValue = CFStringCreateWithCString(kCFAllocatorDefault,
-																		   PyString_AS_STRING(lValue),
-																		   kCFStringEncodingUTF8);
-					CFArrayAppendValue(listHolder, convertedValue);
-					CFRelease(convertedValue);
-				} else if (PyUnicode_Check(lValue)) {
-					CFStringRef convertedValue = CFStringCreateWithBytes(kCFAllocatorDefault,
-																		 (const UInt8 *)PyUnicode_AS_DATA(lValue),
-																		 PyUnicode_GET_DATA_SIZE(lValue),
-																		 kCFStringEncodingUnicode,
-																		 false);
-					CFArrayAppendValue(listHolder, convertedValue);
-					CFRelease(convertedValue);
-				} else {
-					CFRelease(convertedKey);
-					PyErr_SetString(PyExc_TypeError,"The lists must only contain strings");
-					goto error;
-				}
-			}
-			CFDictionarySetValue(note, convertedKey, listHolder);
-			CFRelease(listHolder);
-		} else if (PyObject_HasAttrString(pValue, "rawImageData")) {
-			PyObject *lValue = PyObject_GetAttrString(pValue, "rawImageData");
-			if (!lValue) {
-				goto error;
-			} else if (PyString_Check(lValue)) {
-				CFDataRef convertedValue = CFDataCreate(kCFAllocatorDefault,
-														(const UInt8 *)PyString_AsString(lValue),
-														PyString_Size(lValue));
-				CFDictionarySetValue(note, convertedKey, convertedValue);
-				CFRelease(convertedValue);
-			} else {
-				CFRelease(convertedKey);
-				PyErr_SetString(PyExc_TypeError, "Icon with rawImageData attribute present must ensure it is a string.");
-				goto error;
-			}
-		} else {
-			CFRelease(convertedKey);
-			PyErr_SetString(PyExc_TypeError, "Value is not of Str/List");
-			goto error;
-		}
-		CFRelease(convertedKey);
-	}
-
-	Py_BEGIN_ALLOW_THREADS
-	CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(),
-										 /*name*/ name,
-										 /*object*/ NULL,
-										 /*userInfo*/ note,
-										 /*deliverImmediately*/ false);
-	CFRelease(note);
-	Py_END_ALLOW_THREADS
-
-	Py_DECREF(pKeys);
-
-	Py_INCREF(Py_None);
-	return Py_None;
-
-error:
-	CFRelease(note);
-
-	Py_XDECREF(pKeys);
-
-	return NULL;
-}
-
-static PyObject * growl_PostRegistration(PyObject *self, PyObject *args) {
-	return growl_PostDictionary(CFSTR("GrowlApplicationRegistrationNotification"), self, args);
-}
-
-static PyObject * growl_PostNotification(PyObject *self, PyObject *args) {
-	return growl_PostDictionary(CFSTR("GrowlNotification"), self, args);
-}
-
-static PyMethodDef GrowlMethods[] = {
-	{"PostNotification", growl_PostNotification, METH_VARARGS, "Send a notification to GrowlHelperApp"},
-	{"PostRegistration", growl_PostRegistration, METH_VARARGS, "Send a registration to GrowlHelperApp"},
-	{NULL, NULL, 0, NULL}		/* Sentinel */
-};
-
-
-PyMODINIT_FUNC init_growl(void) {
-	Py_InitModule("_growl", GrowlMethods);
-}

growl-python/trunk/setup.py

-#!/usr/bin/python
-from setuptools import setup, Extension
-import sys
-
-_growl = Extension('_growl',
-                    extra_link_args = ["-framework","CoreFoundation"],
-                    sources = ['libgrowl.c'])
-_growlImage = Extension('_growlImage',
-                        extra_link_args = ["-framework","Cocoa"],
-                        sources = ['growlImage.m'])
-
-if sys.platform.startswith("darwin"):
-    modules = [_growl, _growlImage]
-else:
-    modules = []
-
-setup(name="py-Growl",
-      version="0.0.7",
-      description="Python bindings for posting notifications to the Growl daemon",
-      author="Mark Rowe",
-      author_email="bdash@users.sourceforge.net",
-      url="http://growl.info",
-      py_modules=["Growl"],
-      ext_modules = modules )
-