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