[Console] Refactor main to use async instead of callbacks

Use the new maybe_coroutine decorator to replace callbacks with inline
async/await for cleaner code.
This commit is contained in:
Calum Lind 2023-02-24 23:45:53 +00:00
parent 6c924e6128
commit 253eb2240b
No known key found for this signature in database
GPG Key ID: 90597A687B836BA3
1 changed files with 67 additions and 106 deletions

View File

@ -18,8 +18,7 @@ from twisted.internet import defer, error, reactor
import deluge.common import deluge.common
import deluge.component as component import deluge.component as component
from deluge.configmanager import ConfigManager from deluge.configmanager import ConfigManager
from deluge.decorators import overrides from deluge.decorators import maybe_coroutine, overrides
from deluge.error import DelugeError
from deluge.ui.client import client from deluge.ui.client import client
from deluge.ui.console.modes.addtorrents import AddTorrents from deluge.ui.console.modes.addtorrents import AddTorrents
from deluge.ui.console.modes.basemode import TermResizeHandler from deluge.ui.console.modes.basemode import TermResizeHandler
@ -160,82 +159,54 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
wrapper(self.run) wrapper(self.run)
def quit(self): @maybe_coroutine
async def quit(self):
if client.connected(): if client.connected():
await client.disconnect()
def on_disconnect(result):
reactor.stop()
return client.disconnect().addCallback(on_disconnect)
else:
try: try:
reactor.stop() reactor.stop()
except error.ReactorNotRunning: except error.ReactorNotRunning:
pass pass
def exec_args(self, options): @maybe_coroutine
async def exec_args(self, options):
"""Execute console commands from command line.""" """Execute console commands from command line."""
from deluge.ui.console.cmdline.command import Commander from deluge.ui.console.cmdline.command import Commander
commander = Commander(self._commands) commander = Commander(self._commands)
try:
def on_connect(result): if not self.interactive and options.parsed_cmds[0].command == 'connect':
def on_components_started(result): await commander.exec_command(options.parsed_cmds.pop(0))
def on_started(result):
def do_command(result, cmd):
return commander.do_command(cmd)
def exec_command(result, cmd):
return commander.exec_command(cmd)
d = defer.succeed(None)
for command in options.parsed_cmds:
if command.command in ('quit', 'exit'):
break
d.addCallback(exec_command, command)
d.addCallback(do_command, 'quit')
return d
# We need to wait for the rpcs in start() to finish before processing
# any of the commands.
self.started_deferred.addCallback(on_started)
return self.started_deferred
d = self.start_console()
d.addCallback(on_components_started)
return d
def on_connect_fail(reason):
if reason.check(DelugeError):
rm = reason.getErrorMessage()
else: else:
rm = reason.value.message daemon_options = (
options.daemon_addr,
options.daemon_port,
options.daemon_user,
options.daemon_pass,
)
log.info(
'Connect: host=%s, port=%s, username=%s',
*daemon_options[0:3],
)
await client.connect(*daemon_options)
except Exception as reason:
print( print(
'Could not connect to daemon: %s:%s\n %s' 'Could not connect to daemon: %s:%s\n %s'
% (options.daemon_addr, options.daemon_port, rm) % (options.daemon_addr, options.daemon_port, reason)
) )
commander.do_command('quit') commander.do_command('quit')
d = None await self.start_console()
if not self.interactive and options.parsed_cmds[0].command == 'connect': # Wait for RPCs in start() to finish before processing commands.
d = commander.exec_command(options.parsed_cmds.pop(0)) await self.started_deferred
else:
log.info( for cmd in options.parsed_cmds:
'connect: host=%s, port=%s, username=%s, password=%s', if cmd.command in ('quit', 'exit'):
options.daemon_addr, break
options.daemon_port, await commander.exec_command(cmd)
options.daemon_user,
options.daemon_pass, commander.do_command('quit')
)
d = client.connect(
options.daemon_addr,
options.daemon_port,
options.daemon_user,
options.daemon_pass,
)
d.addCallback(on_connect)
d.addErrback(on_connect_fail)
return d
def run(self, stdscr): def run(self, stdscr):
"""This method is called by the curses.wrapper to start the mainloop and screen. """This method is called by the curses.wrapper to start the mainloop and screen.
@ -353,8 +324,12 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
def is_active_mode(self, mode): def is_active_mode(self, mode):
return mode == self.active_mode return mode == self.active_mode
def start_components(self): @maybe_coroutine
def on_started(result): async def start_components(self):
if not self.interactive:
return await component.start(['SessionProxy', 'ConsoleUI', 'CoreConfig'])
await component.start()
component.pause( component.pause(
[ [
'TorrentList', 'TorrentList',
@ -365,49 +340,35 @@ deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
] ]
) )
if self.interactive: @maybe_coroutine
d = component.start().addCallback(on_started) async def start_console(self):
else:
d = component.start(['SessionProxy', 'ConsoleUI', 'CoreConfig'])
return d
def start_console(self):
# Maintain a list of (torrent_id, name) for use in tab completion
self.started_deferred = defer.Deferred() self.started_deferred = defer.Deferred()
if not self.initialized: if self.initialized:
self.initialized = True await component.stop(['SessionProxy'])
d = self.start_components() await component.start(['SessionProxy'])
else: else:
self.initialized = True
await self.start_components()
def on_stopped(result): @maybe_coroutine
return component.start(['SessionProxy']) async def start(self):
result = await client.core.get_session_state()
d = component.stop(['SessionProxy']).addCallback(on_stopped) # Maintain a list of (torrent_id, name) for use in tab completion
return d
def start(self):
def on_session_state(result):
self.torrents = [] self.torrents = []
self.events = [] self.events = []
def on_torrents_status(torrents): torrents = await client.core.get_torrents_status({'id': result}, ['name'])
for torrent_id, status in torrents.items(): for torrent_id, status in torrents.items():
self.torrents.append((torrent_id, status['name'])) self.torrents.append((torrent_id, status['name']))
self.started_deferred.callback(True) self.started_deferred.callback(True)
client.core.get_torrents_status({'id': result}, ['name']).addCallback(
on_torrents_status
)
d = client.core.get_session_state().addCallback(on_session_state)
# Register event handlers to keep the torrent list up-to-date # Register event handlers to keep the torrent list up-to-date
client.register_event_handler('TorrentAddedEvent', self.on_torrent_added_event) client.register_event_handler('TorrentAddedEvent', self.on_torrent_added_event)
client.register_event_handler( client.register_event_handler(
'TorrentRemovedEvent', self.on_torrent_removed_event 'TorrentRemovedEvent', self.on_torrent_removed_event
) )
return d
def on_torrent_added_event(self, event, from_state=False): def on_torrent_added_event(self, event, from_state=False):
def on_torrent_status(status): def on_torrent_status(status):