Package Skype4Py :: Package api :: Module posix_dbus
[frames] | no frames]

Source Code for Module Skype4Py.api.posix_dbus

  1  """ 
  2  Low level *Skype for Linux* interface implemented using *dbus-python* package. 
  3   
  4  This module handles the options that you can pass to `Skype.__init__` 
  5  for Linux machines when the transport is set to *DBus*. See below. 
  6   
  7  - ``RunMainLoop`` (bool) - If set to False, Skype4Py won't start the GLib main 
  8    loop. Otherwise it is started in a separate thread. The loop must be running for 
  9    Skype4Py events to work properly. Set this option to False if you plan to run the 
 10    loop yourself or if, for example, your GUI framework does it for you. 
 11   
 12  :requires: Skype for Linux 2.0 (beta) or newer. 
 13  """ 
 14  __docformat__ = 'restructuredtext en' 
 15   
 16   
 17  import sys 
 18  import threading 
 19  import time 
 20  import warnings 
 21  import logging 
 22   
 23  from Skype4Py.api import Command, SkypeAPIBase, \ 
 24                           timeout2float, finalize_opts 
 25  from Skype4Py.enums import * 
 26  from Skype4Py.errors import SkypeAPIError 
 27  from Skype4Py.utils import cndexp 
 28   
 29   
 30  __all__ = ['SkypeAPI'] 
 31   
 32   
 33  if getattr(sys, 'skype4py_setup', False): 
34 # we get here if we're building docs; to let the module import without 35 # exceptions, we emulate the dbus module using a class: 36 - class dbus(object):
37 - class service(object):
38 - class Object(object):
39 pass
40 @staticmethod
41 - def method(*args, **kwargs):
42 return lambda *args, **kwargs: None
43 else: 44 import dbus 45 import dbus.service 46 from dbus.mainloop.glib import DBusGMainLoop 47 import gobject
48 49 50 -class SkypeNotify(dbus.service.Object):
51 """DBus object which exports a Notify method. This will be called by Skype for all 52 notifications with the notification string as a parameter. The Notify method of this 53 class calls in turn the callable passed to the constructor. 54 """ 55
56 - def __init__(self, bus, notify):
57 dbus.service.Object.__init__(self, bus, '/com/Skype/Client') 58 self.notify = notify
59 60 @dbus.service.method(dbus_interface='com.Skype.API.Client')
61 - def Notify(self, com):
62 self.notify(unicode(com))
63
64 65 -class SkypeAPI(SkypeAPIBase):
66 - def __init__(self, opts):
67 self.logger = logging.getLogger('Skype4Py.api.posix_dbus.SkypeAPI') 68 SkypeAPIBase.__init__(self) 69 self.run_main_loop = opts.pop('RunMainLoop', True) 70 finalize_opts(opts) 71 self.skype_in = self.skype_out = self.dbus_name_owner_watch = None 72 73 # initialize glib multithreading support 74 gobject.threads_init() 75 76 # dbus-python calls object.__init__() with arguments passed to SessionBus(), 77 # this throws a warning on newer Python versions; here we suppress it 78 warnings.simplefilter('ignore') 79 try: 80 self.bus = dbus.SessionBus(mainloop=DBusGMainLoop()) 81 finally: 82 warnings.simplefilter('default') 83 84 if self.run_main_loop: 85 self.mainloop = gobject.MainLoop()
86
87 - def run(self):
88 self.logger.info('thread started') 89 if self.run_main_loop: 90 self.mainloop.run() 91 self.logger.info('thread finished')
92
93 - def close(self):
94 if self.run_main_loop: 95 self.mainloop.quit() 96 self.skype_in = self.skype_out = None 97 if self.dbus_name_owner_watch is not None: 98 self.bus.remove_signal_receiver(self.dbus_name_owner_watch) 99 self.dbus_name_owner_watch = None 100 SkypeAPIBase.close(self)
101
102 - def set_friendly_name(self, friendly_name):
103 SkypeAPIBase.set_friendly_name(self, friendly_name) 104 if self.skype_out: 105 self.send_command(Command('NAME %s' % friendly_name))
106
107 - def start_watcher(self):
108 # starts a signal receiver detecting Skype being closed/opened 109 self.dbus_name_owner_watch = self.bus.add_signal_receiver(self.dbus_name_owner_changed, 110 'NameOwnerChanged', 111 'org.freedesktop.DBus', 112 'org.freedesktop.DBus', 113 '/org/freedesktop/DBus', 114 arg0='com.Skype.API')
115
116 - def attach(self, timeout, wait=True):
117 self.acquire() 118 try: 119 try: 120 if not self.isAlive(): 121 self.start_watcher() 122 self.start() 123 except AssertionError: 124 pass 125 try: 126 self.wait = True 127 t = threading.Timer(timeout2float(timeout), lambda: setattr(self, 'wait', False)) 128 if wait: 129 t.start() 130 while self.wait: 131 if not wait: 132 self.wait = False 133 try: 134 if not self.skype_out: 135 self.skype_out = self.bus.get_object('com.Skype.API', '/com/Skype') 136 if not self.skype_in: 137 self.skype_in = SkypeNotify(self.bus, self.notify) 138 except dbus.DBusException: 139 if not wait: 140 break 141 time.sleep(1.0) 142 else: 143 break 144 else: 145 raise SkypeAPIError('Skype attach timeout') 146 finally: 147 t.cancel() 148 command = Command('NAME %s' % self.friendly_name, '', True, timeout) 149 if self.skype_out: 150 self.release() 151 try: 152 self.send_command(command) 153 finally: 154 self.acquire() 155 if command.Reply != 'OK': 156 self.skype_out = None 157 self.set_attachment_status(apiAttachRefused) 158 return 159 self.set_attachment_status(apiAttachSuccess) 160 finally: 161 self.release() 162 command = Command('PROTOCOL %s' % self.protocol, Blocking=True) 163 self.send_command(command) 164 self.protocol = int(command.Reply.rsplit(None, 1)[-1])
165
166 - def is_running(self):
167 try: 168 self.bus.get_object('com.Skype.API', '/com/Skype') 169 return True 170 except dbus.DBusException: 171 return False
172
173 - def startup(self, minimized, nosplash):
174 # options are not supported as of Skype 1.4 Beta for Linux 175 if not self.is_running(): 176 import os 177 if os.fork() == 0: # we're child 178 os.setsid() 179 os.execlp('skype')
180
181 - def shutdown(self):
182 import os 183 from signal import SIGINT 184 fh = os.popen('ps -o %p --no-heading -C skype') 185 pid = fh.readline().strip() 186 fh.close() 187 if pid: 188 os.kill(int(pid), SIGINT) 189 self.skype_in = self.skype_out = None
190
191 - def send_command(self, command):
192 if not self.skype_out: 193 self.attach(command.Timeout) 194 self.push_command(command) 195 self.notifier.sending_command(command) 196 cmd = u'#%d %s' % (command.Id, command.Command) 197 self.logger.debug('sending %s', repr(cmd)) 198 if command.Blocking: 199 if self.run_main_loop: 200 command._event = event = threading.Event() 201 else: 202 command._loop = loop = gobject.MainLoop() 203 command._set = False 204 else: 205 command._timer = timer = threading.Timer(command.timeout2float(), self.pop_command, (command.Id,)) 206 try: 207 result = self.skype_out.Invoke(cmd) 208 except dbus.DBusException, err: 209 raise SkypeAPIError(str(err)) 210 if result.startswith(u'#%d ' % command.Id): 211 self.notify(result) 212 if command.Blocking: 213 if self.run_main_loop: 214 event.wait(command.timeout2float()) 215 if not event.isSet(): 216 raise SkypeAPIError('Skype command timeout') 217 elif not command._set: 218 gobject.timeout_add_seconds(int(command.timeout2float()), loop.quit) 219 loop.run() 220 if not command._set: 221 raise SkypeAPIError('Skype command timeout') 222 else: 223 timer.start()
224
225 - def notify(self, cmd):
226 cmd = unicode(cmd) 227 self.logger.debug('received %s', repr(cmd)) 228 if cmd.startswith(u'#'): 229 p = cmd.find(u' ') 230 command = self.pop_command(int(cmd[1:p])) 231 if command is not None: 232 command.Reply = cmd[p + 1:] 233 if command.Blocking: 234 if self.run_main_loop: 235 command._event.set() 236 else: 237 command._set = True 238 command._loop.quit() 239 else: 240 command._timer.cancel() 241 self.notifier.reply_received(command) 242 else: 243 self.notifier.notification_received(cmd[p + 1:]) 244 else: 245 self.notifier.notification_received(cmd)
246
247 - def dbus_name_owner_changed(self, owned, old_owner, new_owner):
248 self.logger.debug('received dbus name owner changed') 249 if new_owner == '': 250 self.skype_out = None 251 self.set_attachment_status(cndexp((new_owner == ''), 252 apiAttachNotAvailable, 253 apiAttachAvailable))
254