From dab04bcb3a51d33ad90de0ea7cdcf5b93982fa50 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 12 Apr 2021 15:13:15 +1200 Subject: [PATCH] simple replacement for urwid signals wrapper as used in server I think this removes all use of urwid from the server code. --- po/POTFILES.in | 4 +- subiquity/server/controllers/mirror.py | 5 +- subiquity/server/controllers/proxy.py | 4 +- subiquity/server/controllers/refresh.py | 6 +-- subiquity/server/controllers/snaplist.py | 5 +- subiquity/server/server.py | 12 ++--- subiquity/ui/views/tests/test_identity.py | 2 - subiquitycore/controller.py | 9 ---- subiquitycore/controllers/network.py | 2 +- subiquitycore/core.py | 12 +---- subiquitycore/pubsub.py | 27 ++++++++++ subiquitycore/signals.py | 64 ----------------------- subiquitycore/tuicontroller.py | 3 -- 13 files changed, 47 insertions(+), 108 deletions(-) create mode 100644 subiquitycore/pubsub.py delete mode 100644 subiquitycore/signals.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 414dc7ad..c860bff4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -57,8 +57,8 @@ subiquitycore/models/network.py subiquitycore/netplan.py subiquitycore/palette.py subiquitycore/prober.py +subiquitycore/pubsub.py subiquitycore/screen.py -subiquitycore/signals.py subiquitycore/snapd.py subiquitycore/ssh.py subiquitycore/testing/__init__.py @@ -111,6 +111,7 @@ subiquity/models/tests/__init__.py subiquity/models/tests/test_filesystem.py subiquity/models/tests/test_mirror.py subiquity/models/tests/test_subiquity.py +subiquity/models/updates.py subiquity/server/controller.py subiquity/server/controllers/cmdlist.py subiquity/server/controllers/debconf.py @@ -130,6 +131,7 @@ subiquity/server/controllers/reporting.py subiquity/server/controllers/snaplist.py subiquity/server/controllers/ssh.py subiquity/server/controllers/tests/test_keyboard.py +subiquity/server/controllers/updates.py subiquity/server/controllers/userdata.py subiquity/server/controllers/zdev.py subiquity/server/dryrun.py diff --git a/subiquity/server/controllers/mirror.py b/subiquity/server/controllers/mirror.py index 754d80f2..2511d928 100644 --- a/subiquity/server/controllers/mirror.py +++ b/subiquity/server/controllers/mirror.py @@ -55,15 +55,14 @@ class MirrorController(SubiquityController): }, } model_name = "mirror" - signals = [ - ('snapd-network-change', 'snapd_network_changed'), - ] def __init__(self, app): super().__init__(app) self.geoip_enabled = True self.check_state = CheckState.NOT_STARTED self.lookup_task = SingleInstanceTask(self.lookup) + self.app.hub.subscribe( + 'snapd-network-change', self.snapd_network_changed) def load_autoinstall_data(self, data): if data is None: diff --git a/subiquity/server/controllers/proxy.py b/subiquity/server/controllers/proxy.py index 5e61a913..0ed64d9d 100644 --- a/subiquity/server/controllers/proxy.py +++ b/subiquity/server/controllers/proxy.py @@ -42,7 +42,7 @@ class ProxyController(SubiquityController): if self.model.proxy: os.environ['http_proxy'] = os.environ['https_proxy'] = \ self.model.proxy - self.signal.emit_signal('network-proxy-set') + self.app.hub.broadcast('network-proxy-set') @with_context() async def apply_autoinstall_config(self, context=None): @@ -64,5 +64,5 @@ class ProxyController(SubiquityController): async def POST(self, data: str): self.model.proxy = data - self.signal.emit_signal('network-proxy-set') + self.app.hub.broadcast('network-proxy-set') self.configured() diff --git a/subiquity/server/controllers/refresh.py b/subiquity/server/controllers/refresh.py index 69cb6256..69f50933 100644 --- a/subiquity/server/controllers/refresh.py +++ b/subiquity/server/controllers/refresh.py @@ -52,10 +52,6 @@ class RefreshController(SubiquityController): 'additionalProperties': False, } - signals = [ - ('snapd-network-change', 'snapd_network_changed'), - ] - def __init__(self, app): super().__init__(app) self.ai_data = {} @@ -63,6 +59,8 @@ class RefreshController(SubiquityController): self.configure_task = None self.check_task = None self.status = RefreshStatus(availability=RefreshCheckState.UNKNOWN) + self.app.hub.subscribe( + 'snapd-network-change', self.snapd_network_changed) def load_autoinstall_data(self, data): if data is not None: diff --git a/subiquity/server/controllers/snaplist.py b/subiquity/server/controllers/snaplist.py index c8793d7f..164226f9 100644 --- a/subiquity/server/controllers/snaplist.py +++ b/subiquity/server/controllers/snaplist.py @@ -129,9 +129,6 @@ class SnapListController(SubiquityController): }, } model_name = "snaplist" - signals = [ - ('snapd-network-change', 'snapd_network_changed'), - ] def _make_loader(self): return SnapdSnapInfoLoader( @@ -141,6 +138,8 @@ class SnapListController(SubiquityController): def __init__(self, app): super().__init__(app) self.loader = self._make_loader() + self.app.hub.subscribe( + 'snapd-network-change', self.snapd_network_changed) def load_autoinstall_data(self, ai_data): to_install = [] diff --git a/subiquity/server/server.py b/subiquity/server/server.py index 0cc82ef6..531f26ba 100644 --- a/subiquity/server/server.py +++ b/subiquity/server/server.py @@ -235,10 +235,10 @@ class SubiquityServer(Application): self.note_data_for_apport("SnapUpdated", str(self.updated)) self.event_listeners = [] self.autoinstall_config = None - self.signal.connect_signals([ - ('network-proxy-set', lambda: schedule_task(self._proxy_set())), - ('network-change', self._network_change), - ]) + self.hub.subscribe( + 'network-up', self._network_change) + self.hub.subscribe( + 'network-proxy-set', lambda: schedule_task(self._proxy_set())) def load_serialized_state(self): for controller in self.controllers.instances: @@ -523,12 +523,12 @@ class SubiquityServer(Application): await self.apply_autoinstall_config() def _network_change(self): - self.signal.emit_signal('snapd-network-change') + self.hub.broadcast('snapd-network-change') async def _proxy_set(self): await run_in_thread( self.snapd.connection.configure_proxy, self.base_model.proxy) - self.signal.emit_signal('snapd-network-change') + self.hub.broadcast('snapd-network-change') def restart(self): cmdline = ['snap', 'run', 'subiquity.subiquity-server'] diff --git a/subiquity/ui/views/tests/test_identity.py b/subiquity/ui/views/tests/test_identity.py index 55edae49..d5bb5192 100644 --- a/subiquity/ui/views/tests/test_identity.py +++ b/subiquity/ui/views/tests/test_identity.py @@ -1,7 +1,6 @@ import unittest from unittest import mock -from subiquitycore.signals import Signal from subiquitycore.testing import view_helpers from subiquity.client.controllers.identity import IdentityController @@ -22,7 +21,6 @@ class IdentityViewTests(unittest.TestCase): def make_view(self): controller = mock.create_autospec(spec=IdentityController) - controller.signal = mock.create_autospec(spec=Signal) return IdentityView(controller, IdentityData()) def test_initial_focus(self): diff --git a/subiquitycore/controller.py b/subiquitycore/controller.py index 968dcf64..ef7bae86 100644 --- a/subiquitycore/controller.py +++ b/subiquitycore/controller.py @@ -22,25 +22,16 @@ log = logging.getLogger("subiquitycore.controller") class BaseController(ABC): """Base class for controllers.""" - signals = [] model_name = None def __init__(self, app): self.name = type(self).__name__[:-len("Controller")] - self.signal = app.signal self.opts = app.opts self.app = app self.context = self.app.context.child(self.name, childlevel="DEBUG") if self.model_name is not None: self.model = getattr(self.app.base_model, self.model_name) - def register_signals(self): - """Defines signals associated with controller from model.""" - signals = [] - for sig, cb in self.signals: - signals.append((sig, getattr(self, cb))) - self.signal.connect_signals(signals) - def start(self): """Called just before the main loop is started. diff --git a/subiquitycore/controllers/network.py b/subiquitycore/controllers/network.py index 8394c717..97192c13 100644 --- a/subiquitycore/controllers/network.py +++ b/subiquitycore/controllers/network.py @@ -470,7 +470,7 @@ class BaseNetworkController(BaseController): @abc.abstractmethod def update_default_routes(self, routes): if routes: - self.signal.emit_signal('network-change') + self.app.hub.broadcast('network-up') @abc.abstractmethod def new_link(self, netdev): diff --git a/subiquitycore/core.py b/subiquitycore/core.py index 9045d54e..22d24c7a 100644 --- a/subiquitycore/core.py +++ b/subiquitycore/core.py @@ -22,7 +22,7 @@ from subiquitycore.context import ( Context, ) from subiquitycore.controllerset import ControllerSet -from subiquitycore.signals import Signal +from subiquitycore.pubsub import MessageHub log = logging.getLogger('subiquitycore.core') @@ -69,7 +69,7 @@ class Application: self.scale_factor = float( os.environ.get('SUBIQUITY_REPLAY_TIMESCALE', "1")) self.updated = os.path.exists(self.state_path('updating')) - self.signal = Signal() + self.hub = MessageHub() self.aio_loop = asyncio.get_event_loop() self.aio_loop.set_exception_handler(self._exception_handler) self.controllers = ControllerSet( @@ -84,13 +84,6 @@ class Application: else: loop.default_exception_handler(context) - def _connect_base_signals(self): - """Connect signals used in the core controller.""" - # Registers signals from each controller - for controller in self.controllers.instances: - controller.register_signals() - log.debug("known signals: %s", self.signal.known_signals) - def state_path(self, *parts): return os.path.join(self.state_dir, *parts) @@ -124,7 +117,6 @@ class Application: async def start(self): self.controllers.load_all() - self._connect_base_signals() self.start_controllers() def run(self): diff --git a/subiquitycore/pubsub.py b/subiquitycore/pubsub.py new file mode 100644 index 00000000..b882cc52 --- /dev/null +++ b/subiquitycore/pubsub.py @@ -0,0 +1,27 @@ +# Copyright 2021 Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +class MessageHub: + + def __init__(self): + self.subscriptions = {} + + def subscribe(self, channel, method, *args): + self.subscriptions.setdefault(channel, []).append((method, args)) + + def broadcast(self, channel): + for m, args in self.subscriptions.get(channel, []): + m(*args) diff --git a/subiquitycore/signals.py b/subiquitycore/signals.py deleted file mode 100644 index ad8b7c37..00000000 --- a/subiquitycore/signals.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2015 Canonical, Ltd. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - - -""" Registers all known signal emitters -""" -import logging -import types - -import urwid - -log = logging.getLogger('subiquity.signals') - - -class SignalException(Exception): - "Problem with a signal" - - -class Signal: - known_signals = [] - - def register_signals(self, signals): - if type(signals) is list: - self.known_signals.extend(signals) - else: - self.known_signals.append(signals) - urwid.register_signal(Signal, self.known_signals) - - def emit_signal(self, name, *args, **kwargs): - urwid.emit_signal(self, name, *args, **kwargs) - - def connect_signal(self, name, cb): - if isinstance(cb, types.MethodType): - scb = "{}.{}".format( - cb.__self__.__class__.__name__, cb.__func__.__name__) - else: - scb = str(cb) - log.debug("connect_signal: %s -> %s", name, scb) - urwid.connect_signal(self, name, cb) - - def connect_signals(self, signal_callback): - """ Connects a batch of signals - - :param list signal_callback: List of tuples eg. ('signame', self.cb) - """ - if not type(signal_callback) is list: - raise SignalException( - "Passed something other than a required list.") - for sig, cb in signal_callback: - if sig not in self.known_signals: - self.register_signals(sig) - self.connect_signal(sig, cb) diff --git a/subiquitycore/tuicontroller.py b/subiquitycore/tuicontroller.py index a5fce566..e3f72a46 100644 --- a/subiquitycore/tuicontroller.py +++ b/subiquitycore/tuicontroller.py @@ -98,9 +98,6 @@ class RepeatedController(BaseController): self.index = index self.context = orig.context - def register_signals(self): - pass - def make_ui(self): return self.orig.make_ui(self.index)