simple replacement for urwid signals wrapper as used in server

I think this removes all use of urwid from the server code.
This commit is contained in:
Michael Hudson-Doyle 2021-04-12 15:13:15 +12:00
parent ddf9baf19c
commit dab04bcb3a
13 changed files with 47 additions and 108 deletions

View File

@ -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

View File

@ -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:

View File

@ -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()

View File

@ -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:

View File

@ -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 = []

View File

@ -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']

View File

@ -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):

View File

@ -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.

View File

@ -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):

View File

@ -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):

27
subiquitycore/pubsub.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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)

View File

@ -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 <http://www.gnu.org/licenses/>.
""" 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)

View File

@ -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)