make the snaplist code more async
This commit is contained in:
parent
7e7ca080f6
commit
4c92e2dd53
|
@ -37,80 +37,64 @@ class SnapdSnapInfoLoader:
|
|||
self.model = model
|
||||
self.store_section = store_section
|
||||
|
||||
self._running = False
|
||||
self.main_task = None
|
||||
self.snap_list_fetched = False
|
||||
self.failed = False
|
||||
|
||||
self.snapd = snapd
|
||||
self.pending_info_snaps = []
|
||||
self.ongoing = {} # {snap:[callbacks]}
|
||||
self.tasks = {} # {snap:task}
|
||||
|
||||
def start(self):
|
||||
self._running = True
|
||||
log.debug("loading list of snaps")
|
||||
schedule_task(self._start())
|
||||
self.main_task = schedule_task(self._start())
|
||||
|
||||
async def _start(self):
|
||||
self.ongoing[None] = []
|
||||
task = self.tasks[None] = schedule_task(self._load_list())
|
||||
await task
|
||||
self.pending_snaps = self.model.get_snap_list()
|
||||
log.debug("fetched list of %s snaps", len(self.pending_snaps))
|
||||
while self.pending_snaps:
|
||||
snap = self.pending_snaps.pop(0)
|
||||
task = self.tasks[snap] = schedule_task(
|
||||
self._fetch_info_for_snap(snap))
|
||||
await task
|
||||
|
||||
async def _load_list(self):
|
||||
try:
|
||||
result = await self.snapd.get(
|
||||
'v2/find', section=self.store_section)
|
||||
except requests.exceptions.RequestException:
|
||||
log.exception("loading list of snaps failed")
|
||||
self.failed = True
|
||||
self._running = False
|
||||
return
|
||||
if not self._running:
|
||||
return
|
||||
self.model.load_find_data(result)
|
||||
self.snap_list_fetched = True
|
||||
self.pending_snaps = self.model.get_snap_list()
|
||||
log.debug("fetched list of %s snaps", len(self.model.get_snap_list()))
|
||||
for cb in self.ongoing.pop(None):
|
||||
cb()
|
||||
while self.pending_snaps and self._running:
|
||||
snap = self.pending_snaps.pop(0)
|
||||
self.ongoing[snap] = []
|
||||
await self._fetch_info_for_snap(snap)
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
if self.main_task is not None:
|
||||
self.main_task.cancel()
|
||||
|
||||
async def _fetch_info_for_snap(self, snap):
|
||||
log.debug('starting fetch for %s', snap.name)
|
||||
try:
|
||||
data = await self.snapd.get(
|
||||
'v2/find', name=snap.name)
|
||||
data = await self.snapd.get('v2/find', name=snap.name)
|
||||
except requests.exceptions.RequestException:
|
||||
log.exception("loading snap info failed")
|
||||
# XXX something better here?
|
||||
return
|
||||
if not self._running:
|
||||
return
|
||||
log.debug('got data for %s', snap.name)
|
||||
self.model.load_info_data(data)
|
||||
for cb in self.ongoing.pop(snap):
|
||||
cb()
|
||||
|
||||
def get_snap_list(self, callback):
|
||||
if self.snap_list_fetched:
|
||||
callback()
|
||||
elif None in self.ongoing:
|
||||
self.ongoing[None].append(callback)
|
||||
else:
|
||||
self.start()
|
||||
self.ongoing[None].append(callback)
|
||||
def get_snap_list_task(self):
|
||||
return self.tasks[None]
|
||||
|
||||
def get_snap_info(self, snap, callback):
|
||||
if len(snap.channels) > 0:
|
||||
callback()
|
||||
return
|
||||
if snap not in self.ongoing:
|
||||
def get_snap_info_task(self, snap):
|
||||
if snap not in self.tasks:
|
||||
if snap in self.pending_snaps:
|
||||
self.pending_snaps.remove(snap)
|
||||
self.ongoing[snap] = []
|
||||
schedule_task(self._fetch_info_for_snap(snap))
|
||||
self.ongoing[snap].append(callback)
|
||||
self.tasks[snap] = schedule_task(self._fetch_info_for_snap(snap))
|
||||
return self.tasks[snap]
|
||||
|
||||
|
||||
class SnapListController(BaseController):
|
||||
|
@ -153,11 +137,11 @@ class SnapListController(BaseController):
|
|||
return
|
||||
self.ui.set_body(SnapListView(self.model, self))
|
||||
|
||||
def get_snap_list(self, callback):
|
||||
self.loader.get_snap_list(callback)
|
||||
def get_snap_list_task(self):
|
||||
return self.loader.get_snap_list_task()
|
||||
|
||||
def get_snap_info(self, snap, callback):
|
||||
self.loader.get_snap_info(snap, callback)
|
||||
def get_snap_info_task(self, snap):
|
||||
return self.loader.get_snap_info_task(snap)
|
||||
|
||||
def done(self, snaps_to_install):
|
||||
log.debug(
|
||||
|
|
|
@ -146,6 +146,7 @@ class FakeSnapdConnection:
|
|||
"Don't know how to fake POST response to {}".format((path, args)))
|
||||
|
||||
def get(self, path, **args):
|
||||
time.sleep(1)
|
||||
filename = path.replace('/', '-')
|
||||
if args:
|
||||
filename += '-' + urlencode(sorted(args.items()))
|
||||
|
|
|
@ -29,6 +29,7 @@ from urwid import (
|
|||
Text,
|
||||
)
|
||||
|
||||
from subiquitycore.async_helpers import schedule_task
|
||||
from subiquitycore.ui.buttons import ok_btn, cancel_btn, other_btn
|
||||
from subiquitycore.ui.container import (
|
||||
Columns,
|
||||
|
@ -313,36 +314,37 @@ class SnapCheckBox(CheckBox):
|
|||
self.snap = snap
|
||||
super().__init__(snap.name, on_state_change=self.state_change)
|
||||
|
||||
def load_info(self):
|
||||
called = False
|
||||
fi = None
|
||||
def loaded(self):
|
||||
if len(self.snap.channels) == 0: # or other indication of failure
|
||||
ff = FetchingFailed(self, self.snap)
|
||||
self.parent.show_overlay(ff, width=ff.width)
|
||||
else:
|
||||
cur_chan = None
|
||||
if self.snap.name in self.parent.to_install:
|
||||
cur_chan = self.parent.to_install[self.snap.name].channel
|
||||
siv = SnapInfoView(self.parent, self.snap, cur_chan)
|
||||
self.parent.show_screen(screen(
|
||||
siv,
|
||||
[other_btn(
|
||||
label=_("Close"),
|
||||
on_press=self.parent.show_main_screen)],
|
||||
focus_buttons=False))
|
||||
|
||||
def callback():
|
||||
nonlocal called
|
||||
called = True
|
||||
if fi is not None:
|
||||
fi.close()
|
||||
if len(self.snap.channels) == 0: # or other indication of failure
|
||||
ff = FetchingFailed(self, self.snap)
|
||||
self.parent.show_overlay(ff, width=ff.width)
|
||||
else:
|
||||
cur_chan = None
|
||||
if self.snap.name in self.parent.to_install:
|
||||
cur_chan = self.parent.to_install[self.snap.name].channel
|
||||
siv = SnapInfoView(self.parent, self.snap, cur_chan)
|
||||
self.parent.show_screen(screen(
|
||||
siv,
|
||||
[other_btn(
|
||||
label=_("Close"),
|
||||
on_press=self.parent.show_main_screen)],
|
||||
focus_buttons=False))
|
||||
self.parent.controller.get_snap_info(self.snap, callback)
|
||||
# If we didn't get callback synchronously, display a dialog
|
||||
# while the info loads.
|
||||
if not called:
|
||||
fi = FetchingInfo(
|
||||
self.parent, self.snap, self.parent.controller.loop)
|
||||
self.parent.show_overlay(fi, width=fi.width)
|
||||
async def wait(self, t, fi):
|
||||
await t
|
||||
fi.close()
|
||||
self.loaded()
|
||||
|
||||
def load_info(self):
|
||||
t = self.parent.controller.get_snap_info_task(self.snap)
|
||||
|
||||
if t.done():
|
||||
self.loaded()
|
||||
return
|
||||
fi = FetchingInfo(
|
||||
self.parent, self.snap, self.parent.controller.loop)
|
||||
self.parent.show_overlay(fi, width=fi.width)
|
||||
schedule_task(self.wait(t, fi))
|
||||
|
||||
def keypress(self, size, key):
|
||||
if key.startswith("enter"):
|
||||
|
@ -371,29 +373,30 @@ class SnapListView(BaseView):
|
|||
self.to_install = {} # {snap_name: (channel, is_classic)}
|
||||
self.load()
|
||||
|
||||
def load(self, sender=None):
|
||||
spinner = None
|
||||
called = False
|
||||
def loaded(self):
|
||||
snap_list = self.model.get_snap_list()
|
||||
if len(snap_list) == 0:
|
||||
self.offer_retry()
|
||||
else:
|
||||
self.make_main_screen(snap_list)
|
||||
self.show_main_screen()
|
||||
|
||||
def callback():
|
||||
nonlocal called
|
||||
called = True
|
||||
if spinner is not None:
|
||||
spinner.stop()
|
||||
snap_list = self.model.get_snap_list()
|
||||
if len(snap_list) == 0:
|
||||
self.offer_retry()
|
||||
else:
|
||||
self.make_main_screen(snap_list)
|
||||
self.show_main_screen()
|
||||
self.controller.get_snap_list(callback)
|
||||
if called:
|
||||
async def _wait(self, t, spinner):
|
||||
spinner.stop()
|
||||
await t
|
||||
self.loaded()
|
||||
|
||||
def load(self, sender=None):
|
||||
t = self.controller.get_snap_list_task()
|
||||
if t.done():
|
||||
self.loaded()
|
||||
return
|
||||
spinner = Spinner(self.controller.loop, style='dots')
|
||||
spinner.start()
|
||||
self._w = screen(
|
||||
[spinner], [ok_btn(label=_("Continue"), on_press=self.done)],
|
||||
excerpt=_("Loading server snaps from store, please wait..."))
|
||||
schedule_task(self._wait(t, spinner))
|
||||
|
||||
def offer_retry(self):
|
||||
self._w = screen(
|
||||
|
|
Loading…
Reference in New Issue