diff --git a/content/_code-samples/build-a-wallet/py/2_threaded.py b/content/_code-samples/build-a-wallet/py/2_threaded.py index 03b7884d0c..895705a66f 100644 --- a/content/_code-samples/build-a-wallet/py/2_threaded.py +++ b/content/_code-samples/build-a-wallet/py/2_threaded.py @@ -14,13 +14,15 @@ class XRPLMonitorThread(Thread): 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. """ - def __init__(self, url, gui, loop): + def __init__(self, url, gui): Thread.__init__(self, daemon=True) # Note: For thread safety, this thread should treat self.gui as # read-only; to modify the GUI, use wx.CallAfter(...) self.gui = gui 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): """ @@ -28,7 +30,6 @@ class XRPLMonitorThread(Thread): from the XRPL, sending them to the GUI thread when necessary, and also handles making requests to the XRPL when the GUI prompts them. """ - asyncio.set_event_loop(self.loop) self.loop.run_forever() async def watch_xrpl(self): @@ -71,8 +72,7 @@ class TWaXLFrame(wx.Frame): self.build_ui() # Start background thread for updates from the ledger ------------------ - self.worker_loop = asyncio.new_event_loop() - self.worker = XRPLMonitorThread(url, self, self.worker_loop) + self.worker = XRPLMonitorThread(url, self) self.worker.start() 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. 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): """ diff --git a/content/_code-samples/build-a-wallet/py/3_account.py b/content/_code-samples/build-a-wallet/py/3_account.py index 0efb1f6b61..875cbacbe2 100644 --- a/content/_code-samples/build-a-wallet/py/3_account.py +++ b/content/_code-samples/build-a-wallet/py/3_account.py @@ -15,13 +15,15 @@ class XRPLMonitorThread(Thread): 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. """ - def __init__(self, url, gui, loop): + def __init__(self, url, gui): Thread.__init__(self, daemon=True) # Note: For thread safety, this thread should treat self.gui as # read-only; to modify the GUI, use wx.CallAfter(...) self.gui = gui 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): """ @@ -29,7 +31,6 @@ class XRPLMonitorThread(Thread): from the XRPL, sending them to the GUI thread when necessary, and also handles making requests to the XRPL when the GUI prompts them. """ - asyncio.set_event_loop(self.loop) self.loop.run_forever() async def watch_xrpl_account(self, address, wallet=None): @@ -126,8 +127,7 @@ class TWaXLFrame(wx.Frame): self.classic_address = address # Start background thread for updates from the ledger ------------------ - self.worker_loop = asyncio.new_event_loop() - self.worker = XRPLMonitorThread(url, self, self.worker_loop) + self.worker = XRPLMonitorThread(url, self) self.worker.start() 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. 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): """ diff --git a/content/_code-samples/build-a-wallet/py/4_tx_history.py b/content/_code-samples/build-a-wallet/py/4_tx_history.py index 4c2c142cec..1e80e65da6 100644 --- a/content/_code-samples/build-a-wallet/py/4_tx_history.py +++ b/content/_code-samples/build-a-wallet/py/4_tx_history.py @@ -18,13 +18,15 @@ class XRPLMonitorThread(Thread): 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. """ - def __init__(self, url, gui, loop): + def __init__(self, url, gui): Thread.__init__(self, daemon=True) # Note: For thread safety, this thread should treat self.gui as # read-only; to modify the GUI, use wx.CallAfter(...) self.gui = gui 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): """ @@ -32,7 +34,6 @@ class XRPLMonitorThread(Thread): from the XRPL, sending them to the GUI thread when necessary, and also handles making requests to the XRPL when the GUI prompts them. """ - asyncio.set_event_loop(self.loop) self.loop.run_forever() async def watch_xrpl_account(self, address, wallet=None): @@ -137,8 +138,7 @@ class TWaXLFrame(wx.Frame): self.classic_address = address # Start background thread for updates from the ledger ------------------ - self.worker_loop = asyncio.new_event_loop() - self.worker = XRPLMonitorThread(url, self, self.worker_loop) + self.worker = XRPLMonitorThread(url, self) self.worker.start() 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. 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): """ diff --git a/content/_code-samples/build-a-wallet/py/5_send_xrp.py b/content/_code-samples/build-a-wallet/py/5_send_xrp.py index d2a3258c1f..e555bec438 100644 --- a/content/_code-samples/build-a-wallet/py/5_send_xrp.py +++ b/content/_code-samples/build-a-wallet/py/5_send_xrp.py @@ -18,13 +18,15 @@ class XRPLMonitorThread(Thread): 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. """ - def __init__(self, url, gui, loop): + def __init__(self, url, gui): Thread.__init__(self, daemon=True) # Note: For thread safety, this thread should treat self.gui as # read-only; to modify the GUI, use wx.CallAfter(...) self.gui = gui 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): """ @@ -32,7 +34,6 @@ class XRPLMonitorThread(Thread): from the XRPL, sending them to the GUI thread when necessary, and also handles making requests to the XRPL when the GUI prompts them. """ - asyncio.set_event_loop(self.loop) self.loop.run_forever() async def watch_xrpl_account(self, address, wallet=None): @@ -246,8 +247,7 @@ class TWaXLFrame(wx.Frame): self.classic_address = address # Start background thread for updates from the ledger ------------------ - self.worker_loop = asyncio.new_event_loop() - self.worker = XRPLMonitorThread(url, self, self.worker_loop) + self.worker = XRPLMonitorThread(url, self) self.worker.start() 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. 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): """ diff --git a/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py b/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py index e0ffd6aafe..f8bcfc6828 100644 --- a/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py +++ b/content/_code-samples/build-a-wallet/py/6_verification_and_polish.py @@ -20,13 +20,15 @@ class XRPLMonitorThread(Thread): 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. """ - def __init__(self, url, gui, loop): + def __init__(self, url, gui): Thread.__init__(self, daemon=True) # Note: For thread safety, this thread should treat self.gui as # read-only; to modify the GUI, use wx.CallAfter(...) self.gui = gui 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): """ @@ -34,7 +36,6 @@ class XRPLMonitorThread(Thread): from the XRPL, sending them to the GUI thread when necessary, and also handles making requests to the XRPL when the GUI prompts them. """ - asyncio.set_event_loop(self.loop) self.loop.run_forever() async def watch_xrpl_account(self, address, wallet=None): @@ -400,8 +401,7 @@ class TWaXLFrame(wx.Frame): self.classic_address = address # Start background thread for updates from the ledger ------------------ - self.worker_loop = asyncio.new_event_loop() - self.worker = XRPLMonitorThread(url, self, self.worker_loop) + self.worker = XRPLMonitorThread(url, self) self.worker.start() 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. 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): """ diff --git a/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md b/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md index d745855930..c302adfa7a 100644 --- a/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md +++ b/content/tutorials/build-apps/build-a-desktop-wallet-in-python.md @@ -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") }} -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: