-
Notifications
You must be signed in to change notification settings - Fork 33
fix: replace deprecated asyncio event loop patterns #877
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e79aa16
62c8a16
ec75f79
27b5a4f
8daf76f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,6 +45,7 @@ class Tftp(Driver): | |
| _shutdown_event: threading.Event = field(init=False, default_factory=threading.Event) | ||
| _loop_ready: threading.Event = field(init=False, default_factory=threading.Event) | ||
| _loop: Optional[asyncio.AbstractEventLoop] = field(init=False, default=None) | ||
| _startup_error: Optional[BaseException] = field(init=False, default=None) | ||
|
|
||
| def __post_init__(self): | ||
| if hasattr(super(), "__post_init__"): | ||
|
|
@@ -67,28 +68,41 @@ def client(cls) -> str: | |
| return "jumpstarter_driver_tftp.client.TftpServerClient" | ||
|
|
||
| def _start_server(self): | ||
| self._loop = asyncio.new_event_loop() | ||
| asyncio.set_event_loop(self._loop) | ||
| self.server = TftpServer( | ||
| host=self.host, | ||
| port=self.port, | ||
| operator=self.children["storage"]._operator, | ||
| logger=self.logger, | ||
| ) | ||
| try: | ||
| self._loop_ready.set() | ||
| self._loop.run_until_complete(self._run_server()) | ||
| asyncio.run(self._run_server_lifecycle()) | ||
| except Exception as e: | ||
| self.logger.error(f"Error running TFTP server: {e}") | ||
| finally: | ||
| try: | ||
| self._loop.run_until_complete(self._loop.shutdown_asyncgens()) | ||
| self._loop.close() | ||
| except Exception as e: | ||
| self.logger.error(f"Error during event loop cleanup: {e}") | ||
| self._loop = None | ||
| self.logger.info("TFTP server thread completed") | ||
|
|
||
| async def _run_server_lifecycle(self): | ||
| """Set up and run the TFTP server within a proper async context. | ||
|
|
||
| Uses asyncio.run() instead of the deprecated new_event_loop() + | ||
| set_event_loop() + run_until_complete() pattern, which emits | ||
| DeprecationWarning on Python 3.12/3.13 and breaks on 3.14 | ||
| (get_event_loop() raises RuntimeError when no loop is running). | ||
| """ | ||
| try: | ||
| self._loop = asyncio.get_running_loop() | ||
| self.server = TftpServer( | ||
| host=self.host, | ||
| port=self.port, | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed CodeRabbit's suggestion: setup code is now wrapped in Added |
||
| operator=self.children["storage"]._operator, | ||
| logger=self.logger, | ||
| ) | ||
| except Exception as e: | ||
| self._startup_error = e | ||
| raise | ||
| finally: | ||
| # Always unblock start() so it can check _startup_error | ||
| # instead of waiting for the full timeout. | ||
| self._loop_ready.set() | ||
| try: | ||
| await self._run_server() | ||
| finally: | ||
| self._loop = None | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| async def _run_server(self): | ||
| try: | ||
| server_task = asyncio.create_task(self.server.start()) | ||
|
|
@@ -120,6 +134,7 @@ def start(self): | |
|
|
||
| self._shutdown_event.clear() | ||
| self._loop_ready.clear() | ||
| self._startup_error = None | ||
|
|
||
| self.server_thread = threading.Thread(target=self._start_server, daemon=True) | ||
| self.server_thread.start() | ||
|
|
@@ -129,6 +144,10 @@ def start(self): | |
| self.server_thread = None | ||
| raise TftpError("Failed to start TFTP server - event loop initialization timeout") | ||
|
|
||
| if self._startup_error is not None: | ||
| self.server_thread = None | ||
| raise TftpError(f"Failed to start TFTP server: {self._startup_error}") from self._startup_error | ||
|
|
||
| self.logger.info(f"TFTP server started on {self.host}:{self.port}") | ||
|
|
||
| @export | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed mmahut's review: the previous docstring incorrectly claimed
asyncio.Event()needs a running loop on Python 3.14+. Verified from CPython 3.14 source thatEvent.__init__()does no loop access — the loop is bound lazily via_LoopBoundMixin._get_loop()on firstawait.Updated the docstring to state the correct motivation: replacing the deprecated
new_event_loop()+set_event_loop()+run_until_complete()pattern withasyncio.run().There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for confirming, this was just a nitpick comment really!