Improve async setup code

This commit is contained in:
mDuo13
2022-01-31 13:40:18 -08:00
parent 9490cab4eb
commit 222a7760d1
6 changed files with 34 additions and 32 deletions

View File

@@ -14,13 +14,15 @@ class XRPLMonitorThread(Thread):
the main frame to be shown in the UI. Using a thread lets us maintain the the main frame to be shown in the UI. Using a thread lets us maintain the
responsiveness of the UI while doing work in the background. responsiveness of the UI while doing work in the background.
""" """
def __init__(self, url, gui, loop): def __init__(self, url, gui):
Thread.__init__(self, daemon=True) Thread.__init__(self, daemon=True)
# Note: For thread safety, this thread should treat self.gui as # Note: For thread safety, this thread should treat self.gui as
# read-only; to modify the GUI, use wx.CallAfter(...) # read-only; to modify the GUI, use wx.CallAfter(...)
self.gui = gui self.gui = gui
self.url = url self.url = url
self.loop = loop self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.loop.set_debug(True)
def run(self): def run(self):
""" """
@@ -28,7 +30,6 @@ class XRPLMonitorThread(Thread):
from the XRPL, sending them to the GUI thread when necessary, and also from the XRPL, sending them to the GUI thread when necessary, and also
handles making requests to the XRPL when the GUI prompts them. handles making requests to the XRPL when the GUI prompts them.
""" """
asyncio.set_event_loop(self.loop)
self.loop.run_forever() self.loop.run_forever()
async def watch_xrpl(self): async def watch_xrpl(self):
@@ -71,8 +72,7 @@ class TWaXLFrame(wx.Frame):
self.build_ui() self.build_ui()
# Start background thread for updates from the ledger ------------------ # Start background thread for updates from the ledger ------------------
self.worker_loop = asyncio.new_event_loop() self.worker = XRPLMonitorThread(url, self)
self.worker = XRPLMonitorThread(url, self, self.worker_loop)
self.worker.start() self.worker.start()
self.run_bg_job(self.worker.watch_xrpl()) self.run_bg_job(self.worker.watch_xrpl())
@@ -92,7 +92,7 @@ class TWaXLFrame(wx.Frame):
Schedules a job to run asynchronously in the XRPL worker thread. Schedules a job to run asynchronously in the XRPL worker thread.
The job should be a Future (for example, from calling an async function) The job should be a Future (for example, from calling an async function)
""" """
task = asyncio.run_coroutine_threadsafe(job, self.worker_loop) task = asyncio.run_coroutine_threadsafe(job, self.worker.loop)
def update_ledger(self, message): def update_ledger(self, message):
""" """

View File

@@ -15,13 +15,15 @@ class XRPLMonitorThread(Thread):
the main frame to be shown in the UI. Using a thread lets us maintain the the main frame to be shown in the UI. Using a thread lets us maintain the
responsiveness of the UI while doing work in the background. responsiveness of the UI while doing work in the background.
""" """
def __init__(self, url, gui, loop): def __init__(self, url, gui):
Thread.__init__(self, daemon=True) Thread.__init__(self, daemon=True)
# Note: For thread safety, this thread should treat self.gui as # Note: For thread safety, this thread should treat self.gui as
# read-only; to modify the GUI, use wx.CallAfter(...) # read-only; to modify the GUI, use wx.CallAfter(...)
self.gui = gui self.gui = gui
self.url = url self.url = url
self.loop = loop self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.loop.set_debug(True)
def run(self): def run(self):
""" """
@@ -29,7 +31,6 @@ class XRPLMonitorThread(Thread):
from the XRPL, sending them to the GUI thread when necessary, and also from the XRPL, sending them to the GUI thread when necessary, and also
handles making requests to the XRPL when the GUI prompts them. handles making requests to the XRPL when the GUI prompts them.
""" """
asyncio.set_event_loop(self.loop)
self.loop.run_forever() self.loop.run_forever()
async def watch_xrpl_account(self, address, wallet=None): async def watch_xrpl_account(self, address, wallet=None):
@@ -126,8 +127,7 @@ class TWaXLFrame(wx.Frame):
self.classic_address = address self.classic_address = address
# Start background thread for updates from the ledger ------------------ # Start background thread for updates from the ledger ------------------
self.worker_loop = asyncio.new_event_loop() self.worker = XRPLMonitorThread(url, self)
self.worker = XRPLMonitorThread(url, self, self.worker_loop)
self.worker.start() self.worker.start()
self.run_bg_job(self.worker.watch_xrpl_account(address, wallet)) self.run_bg_job(self.worker.watch_xrpl_account(address, wallet))
@@ -166,7 +166,7 @@ class TWaXLFrame(wx.Frame):
Schedules a job to run asynchronously in the XRPL worker thread. Schedules a job to run asynchronously in the XRPL worker thread.
The job should be a Future (for example, from calling an async function) The job should be a Future (for example, from calling an async function)
""" """
task = asyncio.run_coroutine_threadsafe(job, self.worker_loop) task = asyncio.run_coroutine_threadsafe(job, self.worker.loop)
def toggle_dialog_style(self, event): def toggle_dialog_style(self, event):
""" """

View File

@@ -18,13 +18,15 @@ class XRPLMonitorThread(Thread):
the main frame to be shown in the UI. Using a thread lets us maintain the the main frame to be shown in the UI. Using a thread lets us maintain the
responsiveness of the UI while doing work in the background. responsiveness of the UI while doing work in the background.
""" """
def __init__(self, url, gui, loop): def __init__(self, url, gui):
Thread.__init__(self, daemon=True) Thread.__init__(self, daemon=True)
# Note: For thread safety, this thread should treat self.gui as # Note: For thread safety, this thread should treat self.gui as
# read-only; to modify the GUI, use wx.CallAfter(...) # read-only; to modify the GUI, use wx.CallAfter(...)
self.gui = gui self.gui = gui
self.url = url self.url = url
self.loop = loop self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.loop.set_debug(True)
def run(self): def run(self):
""" """
@@ -32,7 +34,6 @@ class XRPLMonitorThread(Thread):
from the XRPL, sending them to the GUI thread when necessary, and also from the XRPL, sending them to the GUI thread when necessary, and also
handles making requests to the XRPL when the GUI prompts them. handles making requests to the XRPL when the GUI prompts them.
""" """
asyncio.set_event_loop(self.loop)
self.loop.run_forever() self.loop.run_forever()
async def watch_xrpl_account(self, address, wallet=None): async def watch_xrpl_account(self, address, wallet=None):
@@ -137,8 +138,7 @@ class TWaXLFrame(wx.Frame):
self.classic_address = address self.classic_address = address
# Start background thread for updates from the ledger ------------------ # Start background thread for updates from the ledger ------------------
self.worker_loop = asyncio.new_event_loop() self.worker = XRPLMonitorThread(url, self)
self.worker = XRPLMonitorThread(url, self, self.worker_loop)
self.worker.start() self.worker.start()
self.run_bg_job(self.worker.watch_xrpl_account(address, wallet)) self.run_bg_job(self.worker.watch_xrpl_account(address, wallet))
@@ -197,7 +197,7 @@ class TWaXLFrame(wx.Frame):
Schedules a job to run asynchronously in the XRPL worker thread. Schedules a job to run asynchronously in the XRPL worker thread.
The job should be a Future (for example, from calling an async function) The job should be a Future (for example, from calling an async function)
""" """
task = asyncio.run_coroutine_threadsafe(job, self.worker_loop) task = asyncio.run_coroutine_threadsafe(job, self.worker.loop)
def toggle_dialog_style(self, event): def toggle_dialog_style(self, event):
""" """

View File

@@ -18,13 +18,15 @@ class XRPLMonitorThread(Thread):
the main frame to be shown in the UI. Using a thread lets us maintain the the main frame to be shown in the UI. Using a thread lets us maintain the
responsiveness of the UI while doing work in the background. responsiveness of the UI while doing work in the background.
""" """
def __init__(self, url, gui, loop): def __init__(self, url, gui):
Thread.__init__(self, daemon=True) Thread.__init__(self, daemon=True)
# Note: For thread safety, this thread should treat self.gui as # Note: For thread safety, this thread should treat self.gui as
# read-only; to modify the GUI, use wx.CallAfter(...) # read-only; to modify the GUI, use wx.CallAfter(...)
self.gui = gui self.gui = gui
self.url = url self.url = url
self.loop = loop self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.loop.set_debug(True)
def run(self): def run(self):
""" """
@@ -32,7 +34,6 @@ class XRPLMonitorThread(Thread):
from the XRPL, sending them to the GUI thread when necessary, and also from the XRPL, sending them to the GUI thread when necessary, and also
handles making requests to the XRPL when the GUI prompts them. handles making requests to the XRPL when the GUI prompts them.
""" """
asyncio.set_event_loop(self.loop)
self.loop.run_forever() self.loop.run_forever()
async def watch_xrpl_account(self, address, wallet=None): async def watch_xrpl_account(self, address, wallet=None):
@@ -246,8 +247,7 @@ class TWaXLFrame(wx.Frame):
self.classic_address = address self.classic_address = address
# Start background thread for updates from the ledger ------------------ # Start background thread for updates from the ledger ------------------
self.worker_loop = asyncio.new_event_loop() self.worker = XRPLMonitorThread(url, self)
self.worker = XRPLMonitorThread(url, self, self.worker_loop)
self.worker.start() self.worker.start()
self.run_bg_job(self.worker.watch_xrpl_account(address, wallet)) self.run_bg_job(self.worker.watch_xrpl_account(address, wallet))
@@ -315,7 +315,7 @@ class TWaXLFrame(wx.Frame):
Schedules a job to run asynchronously in the XRPL worker thread. Schedules a job to run asynchronously in the XRPL worker thread.
The job should be a Future (for example, from calling an async function) The job should be a Future (for example, from calling an async function)
""" """
task = asyncio.run_coroutine_threadsafe(job, self.worker_loop) task = asyncio.run_coroutine_threadsafe(job, self.worker.loop)
def toggle_dialog_style(self, event): def toggle_dialog_style(self, event):
""" """

View File

@@ -20,13 +20,15 @@ class XRPLMonitorThread(Thread):
the main frame to be shown in the UI. Using a thread lets us maintain the the main frame to be shown in the UI. Using a thread lets us maintain the
responsiveness of the UI while doing work in the background. responsiveness of the UI while doing work in the background.
""" """
def __init__(self, url, gui, loop): def __init__(self, url, gui):
Thread.__init__(self, daemon=True) Thread.__init__(self, daemon=True)
# Note: For thread safety, this thread should treat self.gui as # Note: For thread safety, this thread should treat self.gui as
# read-only; to modify the GUI, use wx.CallAfter(...) # read-only; to modify the GUI, use wx.CallAfter(...)
self.gui = gui self.gui = gui
self.url = url self.url = url
self.loop = loop self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.loop.set_debug(True)
def run(self): def run(self):
""" """
@@ -34,7 +36,6 @@ class XRPLMonitorThread(Thread):
from the XRPL, sending them to the GUI thread when necessary, and also from the XRPL, sending them to the GUI thread when necessary, and also
handles making requests to the XRPL when the GUI prompts them. handles making requests to the XRPL when the GUI prompts them.
""" """
asyncio.set_event_loop(self.loop)
self.loop.run_forever() self.loop.run_forever()
async def watch_xrpl_account(self, address, wallet=None): async def watch_xrpl_account(self, address, wallet=None):
@@ -400,8 +401,7 @@ class TWaXLFrame(wx.Frame):
self.classic_address = address self.classic_address = address
# Start background thread for updates from the ledger ------------------ # Start background thread for updates from the ledger ------------------
self.worker_loop = asyncio.new_event_loop() self.worker = XRPLMonitorThread(url, self)
self.worker = XRPLMonitorThread(url, self, self.worker_loop)
self.worker.start() self.worker.start()
self.run_bg_job(self.worker.watch_xrpl_account(address, wallet)) self.run_bg_job(self.worker.watch_xrpl_account(address, wallet))
@@ -469,7 +469,7 @@ class TWaXLFrame(wx.Frame):
Schedules a job to run asynchronously in the XRPL worker thread. Schedules a job to run asynchronously in the XRPL worker thread.
The job should be a Future (for example, from calling an async function) The job should be a Future (for example, from calling an async function)
""" """
task = asyncio.run_coroutine_threadsafe(job, self.worker_loop) task = asyncio.run_coroutine_threadsafe(job, self.worker.loop)
def toggle_dialog_style(self, event): def toggle_dialog_style(self, event):
""" """

View File

@@ -103,9 +103,11 @@ Then, the code for the monitor thread is as follows (put this in the same file a
{{ include_code("_code-samples/build-a-wallet/py/2_threaded.py", language="py", start_with="class XRPLMonitorThread", end_before="class TWaXLFrame") }} {{ include_code("_code-samples/build-a-wallet/py/2_threaded.py", language="py", start_with="class XRPLMonitorThread", end_before="class TWaXLFrame") }}
This code defines a `Thread` subclass for the worker. When the thread is created, it starts an event loop, which means it's waiting for async tasks and functions to be created. The `watch_xrpl()` function is an example of a such a task (which the GUI thread starts when it's ready): connects to the XRP Ledger, then calls the [subscribe method][] to be notified whenever a new ledger is validated. It uses the immediate response _and_ all later subscription stream messages to trigger updates of the GUI. This code defines a `Thread` subclass for the worker. When the thread starts, it sets up an event loop, which waits for async tasks to be created and run. The code uses [asyncio's Debug Mode](https://docs.python.org/3/library/asyncio-dev.html#asyncio-debug-mode) so that the console shows any errors that occur in async tasks.
**Tip:** Define worker jobs like this using `async def` instead of `def` so that you can use the `await` keyword in them; you need to use `await` to get the response to the [`AsyncWebsocketClient.request()` method](https://xrpl-py.readthedocs.io/en/stable/source/xrpl.asyncio.clients.html#xrpl.asyncio.clients.AsyncWebsocketClient.request). Normally, you would also need to use `await` or something similar to get the response to functions defined with `async def` too; but, in this app the `run_bg_job()` helper takes care of that in a different way. The `watch_xrpl()` function is an example of a such a task (which the GUI thread starts when it's ready): it connects to the XRP Ledger, then calls the [subscribe method][] to be notified whenever a new ledger is validated. It uses the immediate response _and_ all later subscription stream messages to trigger updates of the GUI.
**Tip:** Define worker jobs like this using `async def` instead of `def` so that you can use the `await` keyword in them; you need to use `await` to get the response to the [`AsyncWebsocketClient.request()` method](https://xrpl-py.readthedocs.io/en/stable/source/xrpl.asyncio.clients.html#xrpl.asyncio.clients.AsyncWebsocketClient.request). Normally, you would also need to use `await` or something similar to get the response from any function you define with `async def`; but, in this app, the `run_bg_job()` helper takes care of that in a different way.
Update the code for the main thread and GUI frame to look like this: Update the code for the main thread and GUI frame to look like this: