1. Victor Ng
  2. nosegrowl

Commits

Victor Ng  committed bc612ff

init checkin of python growl support

  • Participants
  • Branches default

Comments (0)

Files changed (5)

File .hgignore

View file
+syntax: glob
+
+*.coverage
+*.orig
+*.pyc
+*.swp
+*.db
+*.egg-info
+build
+dist
+

File growl-python/trunk/Growl.py

View file
+"""
+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)

File growl-python/trunk/growlImage.m

View file
+/*
+ * 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);
+}

File growl-python/trunk/libgrowl.c

View file
+/*
+ * 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);
+}

File growl-python/trunk/setup.py

View file
+#!/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 )
+