Bindings/python/Growl.py
author Peter Hosey
Wed Mar 25 04:11:45 2009 -0700 (2009-03-25)
changeset 4182 69b5347d8262
parent 3928 8a7f66f30c17
child 4183 2e72fc363066
permissions -rw-r--r--
Patch by Charles Lepple to fix two of the assertions in our Python bindings.

In Python, assert is a statement, not a function. With the parentheses, Python takes the parentheses and everything between them as a tuple, and uses this as the condition; since it contains two items, it is not empty, which means it is true, which means the assertion would always pass.

The fix is to remove the parentheses. Then, the assertion statement has its usual two arguments (formerly the items in the tuple).
     1 """
     2 A Python module that enables posting notifications to the Growl daemon.
     3 See <http://growl.info/> for more information.
     4 """
     5 __version__ = "0.7" 
     6 __author__ = "Mark Rowe <bdash@users.sourceforge.net>"
     7 __copyright__ = "(C) 2003 Mark Rowe <bdash@users.sourceforge.net>. Released under the BSD license."
     8 __contributors__ = ["Ingmar J Stein (Growl Team)", 
     9 					"Rui Carmo (http://the.taoofmac.com)",
    10 					"Jeremy Rossi <jeremy@jeremyrossi.com>",
    11 					"Peter Hosey <http://boredzo.org/> (Growl Team)",
    12 				   ]
    13 
    14 import _growl
    15 import types
    16 import struct
    17 import md5
    18 import socket
    19 
    20 GROWL_UDP_PORT=9887
    21 GROWL_PROTOCOL_VERSION=1
    22 GROWL_TYPE_REGISTRATION=0
    23 GROWL_TYPE_NOTIFICATION=1
    24 
    25 GROWL_APP_NAME="ApplicationName"
    26 GROWL_APP_ICON="ApplicationIcon"
    27 GROWL_NOTIFICATIONS_DEFAULT="DefaultNotifications"
    28 GROWL_NOTIFICATIONS_ALL="AllNotifications"
    29 GROWL_NOTIFICATIONS_USER_SET="AllowedUserNotifications"
    30 
    31 GROWL_NOTIFICATION_NAME="NotificationName"
    32 GROWL_NOTIFICATION_TITLE="NotificationTitle"
    33 GROWL_NOTIFICATION_DESCRIPTION="NotificationDescription"
    34 GROWL_NOTIFICATION_ICON="NotificationIcon"
    35 GROWL_NOTIFICATION_APP_ICON="NotificationAppIcon"
    36 GROWL_NOTIFICATION_PRIORITY="NotificationPriority"
    37 		
    38 GROWL_NOTIFICATION_STICKY="NotificationSticky"
    39 
    40 GROWL_APP_REGISTRATION="GrowlApplicationRegistrationNotification"
    41 GROWL_APP_REGISTRATION_CONF="GrowlApplicationRegistrationConfirmationNotification"
    42 GROWL_NOTIFICATION="GrowlNotification"
    43 GROWL_SHUTDOWN="GrowlShutdown"
    44 GROWL_PING="Honey, Mind Taking Out The Trash"
    45 GROWL_PONG="What Do You Want From Me, Woman"
    46 GROWL_IS_READY="Lend Me Some Sugar; I Am Your Neighbor!"
    47 
    48 	
    49 growlPriority = {"Very Low":-2,"Moderate":-1,"Normal":0,"High":1,"Emergency":2}
    50 
    51 class netgrowl:
    52 	"""Builds a Growl Network Registration packet.
    53 	   Defaults to emulating the command-line growlnotify utility."""
    54 
    55 	__notAllowed__ = [GROWL_APP_ICON, GROWL_NOTIFICATION_ICON, GROWL_NOTIFICATION_APP_ICON]
    56 
    57 	def __init__(self, hostname, password ):
    58 		self.hostname = hostname
    59 		self.password = password
    60 		self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    61 
    62 	def send(self, data):
    63 		self.socket.sendto(data, (self.hostname, GROWL_UDP_PORT))
    64 		
    65 	def PostNotification(self, userInfo):
    66 		if userInfo.has_key(GROWL_NOTIFICATION_PRIORITY):
    67 			priority = userInfo[GROWL_NOTIFICATION_PRIORITY]
    68 		else:
    69 			priority = 0
    70 		if userInfo.has_key(GROWL_NOTIFICATION_STICKY):
    71 			sticky = userInfo[GROWL_NOTIFICATION_STICKY]
    72 		else:
    73 			sticky = False
    74 		data = self.encodeNotify(userInfo[GROWL_APP_NAME],
    75 								 userInfo[GROWL_NOTIFICATION_NAME],
    76 								 userInfo[GROWL_NOTIFICATION_TITLE],
    77 								 userInfo[GROWL_NOTIFICATION_DESCRIPTION],
    78 								 priority,
    79 								 sticky)
    80 		return self.send(data)
    81 
    82 	def PostRegistration(self, userInfo):
    83 		data = self.encodeRegistration(userInfo[GROWL_APP_NAME],
    84 									   userInfo[GROWL_NOTIFICATIONS_ALL],
    85 									   userInfo[GROWL_NOTIFICATIONS_DEFAULT])
    86 		return self.send(data)
    87 
    88 	def encodeRegistration(self, application, notifications, defaultNotifications):
    89 		data = struct.pack("!BBH",
    90 						   GROWL_PROTOCOL_VERSION,
    91 						   GROWL_TYPE_REGISTRATION,
    92 						   len(application) )
    93 		data += struct.pack("BB",
    94 							len(notifications),
    95 							len(defaultNotifications) )
    96 		data += application
    97 		for i in notifications:
    98 			encoded = i.encode("utf-8")
    99 			data += struct.pack("!H", len(encoded))
   100 			data += encoded
   101 		for i in defaultNotifications:
   102 			data += struct.pack("B", i)
   103 		return self.encodePassword(data)
   104 
   105 	def encodeNotify(self, application, notification, title, description,
   106 					 priority = 0, sticky = False):
   107 
   108 		application  = application.encode("utf-8")
   109 		notification = notification.encode("utf-8")
   110 		title		= title.encode("utf-8")
   111 		description  = description.encode("utf-8")
   112 		flags = (priority & 0x07) * 2
   113 		if priority < 0: 
   114 			flags |= 0x08
   115 		if sticky: 
   116 			flags = flags | 0x0001
   117 		data = struct.pack("!BBHHHHH",
   118 						   GROWL_PROTOCOL_VERSION,
   119 						   GROWL_TYPE_NOTIFICATION,
   120 						   flags,
   121 						   len(notification),
   122 						   len(title),
   123 						   len(description),
   124 						   len(application) )
   125 		data += notification
   126 		data += title
   127 		data += description
   128 		data += application
   129 		return self.encodePassword(data)
   130 
   131 	def encodePassword(self, data):
   132 		checksum = md5.new()
   133 		checksum.update(data)
   134 		if self.password:
   135 		   checksum.update(self.password)
   136 		data += checksum.digest()
   137 		return data
   138 
   139 class _ImageHook(type):
   140 	def __getattribute__(self, attr):
   141 		global Image
   142 		if Image is self:
   143 			from _growlImage import Image
   144 
   145 		return getattr(Image, attr)
   146 
   147 class Image(object):
   148 	__metaclass__ = _ImageHook
   149 
   150 class _RawImage(object):
   151 	def __init__(self, data):  self.rawImageData = data
   152 
   153 class GrowlNotifier(object):
   154 	"""
   155 	A class that abstracts the process of registering and posting
   156 	notifications to the Growl daemon.
   157 
   158 	You can either pass `applicationName', `notifications',
   159 	`defaultNotifications' and `applicationIcon' to the constructor
   160 	or you may define them as class-level variables in a sub-class.
   161 
   162 	`defaultNotifications' is optional, and defaults to the value of
   163 	`notifications'.  `applicationIcon' is also optional but defaults
   164 	to a pointless icon so is better to be specified.
   165 	"""
   166 
   167 	applicationName = 'GrowlNotifier'
   168 	notifications = []
   169 	defaultNotifications = []
   170 	applicationIcon = None
   171 	_notifyMethod = _growl
   172 
   173 	def __init__(self, applicationName=None, notifications=None, defaultNotifications=None, applicationIcon=None, hostname=None, password=None):
   174 		if applicationName:
   175 			self.applicationName = applicationName
   176 		assert self.applicationName, 'An application name is required.'
   177 
   178 		if notifications:
   179 			self.notifications = list(notifications)
   180 		assert self.notifications, 'A sequence of one or more notification names is required.'
   181 
   182 		if defaultNotifications is not None:
   183 			self.defaultNotifications = list(defaultNotifications)
   184 		elif not self.defaultNotifications:
   185 			self.defaultNotifications = list(self.notifications)
   186 
   187 		if applicationIcon is not None:
   188 			self.applicationIcon = self._checkIcon(applicationIcon)
   189 		elif self.applicationIcon is not None:
   190 			self.applicationIcon = self._checkIcon(self.applicationIcon)
   191 
   192 		if hostname is not None and password is not None:
   193 			self._notifyMethod = netgrowl(hostname, password)
   194 		elif hostname is not None or password is not None:
   195 			raise KeyError, "Hostname and Password are both required for a network notification"
   196 
   197 	def _checkIcon(self, data):
   198 		if isinstance(data, str):
   199 			return _RawImage(data)
   200 		else:
   201 			return data
   202 
   203 	def register(self):
   204 		if self.applicationIcon is not None:
   205 			self.applicationIcon = self._checkIcon(self.applicationIcon)
   206 
   207 		regInfo = {GROWL_APP_NAME: self.applicationName,
   208 				   GROWL_NOTIFICATIONS_ALL: self.notifications,
   209 				   GROWL_NOTIFICATIONS_DEFAULT: self.defaultNotifications,
   210 				   GROWL_APP_ICON:self.applicationIcon,
   211 				  }
   212 		self._notifyMethod.PostRegistration(regInfo)
   213 
   214 	def notify(self, noteType, title, description, icon=None, sticky=False, priority=None):
   215 		assert noteType in self.notifications
   216 		notifyInfo = {GROWL_NOTIFICATION_NAME: noteType,
   217 					  GROWL_APP_NAME: self.applicationName,
   218 					  GROWL_NOTIFICATION_TITLE: title,
   219 					  GROWL_NOTIFICATION_DESCRIPTION: description,
   220 					 }
   221 		if sticky:
   222 			notifyInfo[GROWL_NOTIFICATION_STICKY] = 1
   223 
   224 		if priority is not None:
   225 			notifyInfo[GROWL_NOTIFICATION_PRIORITY] = priority
   226 
   227 		if icon:
   228 			notifyInfo[GROWL_NOTIFICATION_ICON] = self._checkIcon(icon)
   229 
   230 		self._notifyMethod.PostNotification(notifyInfo)