From 403eb51ea73a75b02f7b620497814eb0ea8c35c2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 25 Jan 2018 15:55:56 +0100 Subject: [PATCH] kete: have a common worker implementation For the various commands, it is easier if we have a common interface than just a simple function call. Each current command runs something before the mainloop is created, and then something after the mainloop is terminated. Having such a worker allows us to have only one place where we start the mainloop, meaning that the interactive prompt will not try to start it more than once, and most above, will not kill it in the middle of a command while other commands are still running. --- tools/tuhi-kete.py | 133 ++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 55 deletions(-) diff --git a/tools/tuhi-kete.py b/tools/tuhi-kete.py index 2588c4c..8f45fa8 100755 --- a/tools/tuhi-kete.py +++ b/tools/tuhi-kete.py @@ -265,11 +265,47 @@ class TuhiKeteManager(_DBusObject): pass -class Searcher(GObject.Object): - def __init__(self, manager, address=None): +class Worker(GObject.Object): + """Implements a command to be executed. + Subclasses need to overwrite run() that will be executed + to setup the command (before the mainloop). + Subclass can also implement the stop() method which + will be executed to terminate the command, once the + mainloop has finished. + + The variable need_mainloop needs to be set from the + subclass if the command requires the mainloop to be + run from an undetermined amount of time.""" + + need_mainloop = False + + def __init__(self, manager, args=None): GObject.GObject.__init__(self) self.manager = manager - self.address = address + self._run = self.run + self._stop = self.stop + + def run(self): + pass + + def stop(self): + pass + + def start(self): + self._run() + + if self.need_mainloop: + self.manager.run() + + self._stop() + + +class Searcher(Worker): + need_mainloop = True + + def __init__(self, manager, args): + super(Searcher, self).__init__(manager) + self.address = args.address self.is_pairing = False def run(self): @@ -277,27 +313,26 @@ class Searcher(GObject.Object): logger.error('Another client is already searching') return - s1 = self.manager.connect('notify::searching', self._on_notify_search) - s2 = self.manager.connect('pairable-device', self._on_pairable_device) + self.s1 = self.manager.connect('notify::searching', self._on_notify_search) + self.s2 = self.manager.connect('pairable-device', self._on_pairable_device) self.manager.start_search() logger.debug('Started searching') for d in self.manager.devices: self._on_pairable_device(self.manager, d) - self.manager.run() - + def stop(self): if self.manager.searching: logger.debug('Stopping search') self.manager.stop_search() - self.manager.disconnect(s1) - self.manager.disconnect(s2) + self.manager.disconnect(self.s1) + self.manager.disconnect(self.s2) def _on_notify_search(self, manager, pspec): if not manager.searching: logger.info('Search cancelled') if not self.is_pairing: - self.manager.quit() + self.stop() def _on_pairable_device(self, manager, device): print('Pairable device: {}'.format(device)) @@ -318,18 +353,20 @@ class Searcher(GObject.Object): device.pair() -class Listener(GObject.Object): - def __init__(self, manager, address): - GObject.GObject.__init__(self) +class Listener(Worker): + need_mainloop = True + + def __init__(self, manager, args): + super(Listener, self).__init__(manager) - self.manager = manager self.device = None for d in manager.devices: - if d.address == address: + if d.address == args.address: self.device = d break else: - logger.error("{}: device not found".format(address)) + logger.error("{}: device not found".format(args.address)) + # FIXME: this should be an exception return def run(self): @@ -344,16 +381,16 @@ class Listener(GObject.Object): return logger.debug("{}: starting listening".format(self.device)) - s1 = self.device.connect('notify::listening', self._on_device_listening) - s2 = self.device.connect('notify::drawings-available', self._on_drawings_available) + self.s1 = self.device.connect('notify::listening', self._on_device_listening) + self.s2 = self.device.connect('notify::drawings-available', self._on_drawings_available) self.device.start_listening() - self.manager.run() + def stop(self): logger.debug("{}: stopping listening".format(self.device)) try: self.device.stop_listening() - self.device.disconnect(s1) - self.device.disconnect(s2) + self.device.disconnect(self.s1) + self.device.disconnect(self.s2) except GLib.Error as e: if (e.domain != 'g-dbus-error-quark' or e.code != Gio.IOErrorEnum.EXISTS or @@ -364,8 +401,7 @@ class Listener(GObject.Object): if self.device.listening: return - logger.info('{}: Listening stopped, exiting'.format(device)) - self.manager.quit() + logger.info('{}: Listening stopped'.format(device)) def _on_drawings_available(self, device, pspec): self._log_drawings_available(device) @@ -375,12 +411,13 @@ class Listener(GObject.Object): logger.info('{}: drawings available: {}'.format(device, s)) -class Fetcher(GObject.Object): - def __init__(self, manager, address, index): - GObject.GObject.__init__(self) - self.manager = manager +class Fetcher(Worker): + def __init__(self, manager, args): + super(Fetcher, self).__init__(manager) self.device = None self.indices = None + address = args.address + index = args.index for d in manager.devices: if d.address == address: @@ -436,31 +473,16 @@ class Fetcher(GObject.Object): svg.save() -def print_device(d): - print('{}: {}'.format(d.address, d.name)) - - -def cmd_list(manager, args): - logger.debug('Listing available devices:') - for d in manager.devices: - print_device(d) - - -def cmd_pair(manager, args): - Searcher(manager, args.address).run() - - -def cmd_listen(manager, args): - Listener(manager, args.address).run() - - -def cmd_fetch(manager, args): - Fetcher(manager, args.address, args.index).run() +class Printer(Worker): + def run(self): + logger.debug('Listing available devices:') + for d in self.manager.devices: + print(d) def parse_list(parser): sub = parser.add_parser('list', help='list known devices') - sub.set_defaults(func=cmd_list) + sub.set_defaults(worker=Printer) def parse_pair(parser): @@ -469,7 +491,7 @@ def parse_pair(parser): type=TuhiKeteDevice.is_device_address, nargs='?', default=None, help='the address of the device to pair') - sub.set_defaults(func=cmd_pair) + sub.set_defaults(worker=Searcher) def parse_listen(parser): @@ -478,7 +500,7 @@ def parse_listen(parser): type=TuhiKeteDevice.is_device_address, default=None, help='the address of the device to listen to') - sub.set_defaults(func=cmd_listen) + sub.set_defaults(worker=Listener) def parse_fetch(parser): @@ -490,7 +512,7 @@ def parse_fetch(parser): sub.add_argument('index', metavar='[|all]', type=str, default=None, help='the index of the drawing to fetch or a literal "all"') - sub.set_defaults(func=cmd_fetch) + sub.set_defaults(worker=Fetcher) def parse(args): @@ -515,12 +537,13 @@ def main(args): if args.verbose: logger.setLevel(logging.DEBUG) + if not hasattr(args, 'worker'): + args.worker = Printer + try: with TuhiKeteManager() as mgr: - if not hasattr(args, 'func'): - args.func = cmd_list - - args.func(mgr, args) + worker = args.worker(mgr, args) + worker.start() except DBusError as e: logger.error(e.message)