|
8 | 8 |
|
9 | 9 | from __future__ import annotations |
10 | 10 |
|
11 | | -import asyncio |
12 | 11 | from pathlib import Path |
13 | | -from typing import TYPE_CHECKING, TypeVar |
| 12 | +from typing import TYPE_CHECKING |
14 | 13 |
|
15 | 14 | from marimo import _loggers |
16 | 15 | from marimo._cli.sandbox import SandboxMode |
|
48 | 47 | from marimo._session.session_repository import SessionRepository |
49 | 48 | from marimo._session.types import KernelState |
50 | 49 | from marimo._types.ids import ConsumerId, SessionId |
| 50 | +from marimo._utils.asyncio_utils import fire_and_forget |
51 | 51 | from marimo._utils.file_watcher import FileWatcherManager |
52 | 52 |
|
53 | 53 | if TYPE_CHECKING: |
54 | | - from collections.abc import Awaitable, Coroutine, Mapping |
| 54 | + from collections.abc import Mapping |
55 | 55 |
|
56 | 56 | from marimo._session.notebook import AppFileManager |
57 | 57 |
|
@@ -241,7 +241,10 @@ def create_session( |
241 | 241 | self._repository.add_sync(session_id, session) |
242 | 242 |
|
243 | 243 | # Emit session created event (triggers file watcher attachment, recents, etc.) |
244 | | - run_async(self._event_bus.emit_session_created(session)) |
| 244 | + fire_and_forget( |
| 245 | + self._event_bus.emit_session_created(session), |
| 246 | + name="session.created", |
| 247 | + ) |
245 | 248 |
|
246 | 249 | return session |
247 | 250 |
|
@@ -333,10 +336,11 @@ def maybe_resume_session( |
333 | 336 | if resumed_session: |
334 | 337 | # Emit resume event (use new_session_id as both old and new since |
335 | 338 | # the strategy already updated it) |
336 | | - run_async( |
| 339 | + fire_and_forget( |
337 | 340 | self._event_bus.emit_session_resumed( |
338 | 341 | resumed_session, new_session_id |
339 | | - ) |
| 342 | + ), |
| 343 | + name="session.resumed", |
340 | 344 | ) |
341 | 345 |
|
342 | 346 | return resumed_session |
@@ -389,7 +393,10 @@ def close_session(self, session_id: SessionId) -> bool: |
389 | 393 | if session is None: |
390 | 394 | return False |
391 | 395 |
|
392 | | - run_async(self._event_bus.emit_session_closed(session)) |
| 396 | + fire_and_forget( |
| 397 | + self._event_bus.emit_session_closed(session), |
| 398 | + name="session.closed", |
| 399 | + ) |
393 | 400 |
|
394 | 401 | session.close() |
395 | 402 | return True |
@@ -418,28 +425,3 @@ def should_send_code_to_frontend(self) -> bool: |
418 | 425 | def get_active_connection_count(self) -> int: |
419 | 426 | """Get the number of sessions with active connections.""" |
420 | 427 | return len(self._repository.get_active_sessions()) |
421 | | - |
422 | | - |
423 | | -T = TypeVar("T") |
424 | | - |
425 | | - |
426 | | -def run_async(coro: Coroutine[None, None, T] | Awaitable[T]) -> T: |
427 | | - """Run an async coroutine, handling various event loop states. |
428 | | -
|
429 | | - 1. Event loop is running: create a task |
430 | | - 2. Event loop exists but not running: run_until_complete |
431 | | - 3. No event loop: create one with asyncio.run |
432 | | - """ |
433 | | - try: |
434 | | - loop = asyncio.get_event_loop() |
435 | | - if loop.is_running(): |
436 | | - # Create a task and return it (fire and forget) |
437 | | - # Note: This doesn't wait for completion |
438 | | - task = asyncio.create_task(coro) # type: ignore |
439 | | - return task # type: ignore |
440 | | - else: |
441 | | - # Run to completion |
442 | | - return loop.run_until_complete(coro) # type: ignore |
443 | | - except RuntimeError: |
444 | | - # No event loop exists, create one |
445 | | - return asyncio.run(coro) # type: ignore |
0 commit comments