kete: make the shell survive the disappearing of the daemon

We need to move the mainloop in the Shell as we need to watch on the
name there.

Fixes #55
This commit is contained in:
Benjamin Tissoires 2018-01-31 19:22:44 +01:00 committed by Peter Hutterer
parent 277523c7d5
commit 3e444d6a0c
1 changed files with 61 additions and 44 deletions

View File

@ -134,6 +134,9 @@ class _DBusObject(GObject.Object):
return p.unpack()
return p
def terminate(self):
del(self.proxy)
class _DBusSystemObject(_DBusObject):
'''
@ -202,7 +205,7 @@ class TuhiKeteDevice(_DBusObject):
logger.debug(f'{self}: Pairing')
# FIXME: Pair() doesn't return anything useful yet, so we wait until
# the device is in the Manager's Devices property
self.manager.connect('notify::devices', self._on_mgr_devices_updated)
self.s1 = self.manager.connect('notify::devices', self._on_mgr_devices_updated)
self.is_pairing = True
self.proxy.Pair()
@ -251,15 +254,23 @@ class TuhiKeteDevice(_DBusObject):
for d in manager.devices:
if d.address == self.address:
self.is_pairing = False
self.manager.disconnect(self.s1)
del(self.s1)
logger.info(f'{self}: Pairing successful')
def terminate(self):
try:
self.manager.disconnect(self.s1)
except AttributeError:
pass
self._bluez_device.terminate()
super(TuhiKeteDevice, self).terminate()
class TuhiKeteManager(_DBusObject):
__gsignals__ = {
'pairable-device':
(GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT,)),
'dbus-name-vanished':
(GObject.SIGNAL_RUN_FIRST, None, ()),
}
def __init__(self):
@ -267,17 +278,6 @@ class TuhiKeteManager(_DBusObject):
ORG_FREEDESKTOP_TUHI1_MANAGER,
ROOT_PATH)
Gio.bus_watch_name(Gio.BusType.SESSION,
TUHI_DBUS_NAME,
Gio.BusNameWatcherFlags.NONE,
None,
self._on_name_vanished)
# we can not call GLib.MainLoop() here or it will install a unix signal
# handler for SIGINT, and we will not be able to catch
# KeyboardInterrupt in cmdloop()
self.mainloop = GLib.MainLoop.new(None, False)
self._devices = {}
self._pairable_devices = {}
@ -285,10 +285,6 @@ class TuhiKeteManager(_DBusObject):
device = TuhiKeteDevice(self, objpath)
self._devices[device.address] = device
self._glib_thread = threading.Thread(target=self.run)
self._glib_thread.daemon = True
self._glib_thread.start()
@GObject.Property
def devices(self):
return [v for k, v in self._devices.items()]
@ -309,14 +305,12 @@ class TuhiKeteManager(_DBusObject):
self.proxy.StopSearch()
self._pairable_devices = {}
def run(self):
try:
self.mainloop.run()
except KeyboardInterrupt:
self.mainloop.quit()
def quit(self):
self.mainloop.quit()
def terminate(self):
for dev in self._devices.values():
dev.terminate()
self._devices = {}
self._pairable_devices = {}
super(TuhiKeteManager, self).terminate()
def _on_properties_changed(self, proxy, changed_props, invalidated_props):
if changed_props is None:
@ -347,20 +341,9 @@ class TuhiKeteManager(_DBusObject):
logger.debug(f'Found pairable device: {device}')
self.emit('pairable-device', device)
def _on_name_vanished(self, connection, name):
logger.error('Tuhi daemon went away')
self.emit('dbus-name-vanished')
self.mainloop.quit()
def __getitem__(self, btaddr):
return self._devices[btaddr]
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.mainloop.quit()
class Worker(GObject.Object):
'''Implements a command to be executed.
@ -561,25 +544,58 @@ class TuhiKeteShell(cmd.Cmd):
intro = 'Tuhi shell control'
prompt = 'tuhi> '
def __init__(self, manager, completekey='tab', stdin=None, stdout=None):
def __init__(self, completekey='tab', stdin=None, stdout=None):
super(TuhiKeteShell, self).__init__(completekey, stdin, stdout)
self._manager = manager
self._manager = None
self._workers = []
self._log_handler = TuhiKeteShellLogHandler()
logger.removeHandler(logger_handler)
logger.addHandler(self._log_handler)
self._log_handler.set_prompt_mode(self.prompt)
# patching get_names to hide some functions we do not want in the help
self.get_names = self._filtered_get_names
manager.connect('dbus-name-vanished', self._on_name_vanished)
Gio.bus_watch_name(Gio.BusType.SESSION,
TUHI_DBUS_NAME,
Gio.BusNameWatcherFlags.NONE,
self._on_name_appeared,
self._on_name_vanished)
def __enter__(self):
# we can not call GLib.MainLoop() here or it will install a unix signal
# handler for SIGINT, and we will not be able to catch
# KeyboardInterrupt in cmdloop()
self._mainloop = GLib.MainLoop.new(None, False)
self._glib_thread = threading.Thread(target=self._mainloop.run)
self._glib_thread.daemon = True
self._glib_thread.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self._mainloop.quit()
self._glib_thread.join()
def _filtered_get_names(self):
names = super(TuhiKeteShell, self).get_names()
names.remove('do_EOF')
return names
def _on_name_vanished(self, manager):
logger.debug('Tuhi daemon went away, terminating the current workers')
def _on_name_appeared(self, connection, name, client):
logger.debug('Tuhi daemon is up and running')
self._manager = TuhiKeteManager()
def _on_name_vanished(self, connection, name):
if self._manager is not None:
logger.error('Tuhi daemon went away')
else:
logger.warning('Tuhi daemon not started')
self.terminate_workers()
if self._manager is not None:
self._manager.terminate()
self._manager = None
def emptyline(self):
# make sure we do not re-enter the last typed command
@ -597,6 +613,8 @@ class TuhiKeteShell(cmd.Cmd):
def precmd(self, line):
# Restore the logger facility to something sane:
self._log_handler.set_normal_mode()
if self._manager is None and line not in ['EOF', 'exit']:
return ''
return line
def postcmd(self, stop, line):
@ -975,8 +993,7 @@ def main(args):
logger.setLevel(logging.DEBUG)
try:
with TuhiKeteManager() as mgr:
shell = TuhiKeteShell(mgr)
with TuhiKeteShell() as shell:
shell.run()
except DBusError as e: