/ src / solace_agent_mesh / common / utils / asyncio_macos_fix.py
asyncio_macos_fix.py
 1  """
 2  Minimal asyncio fix for macOS subprocess creation issues.
 3  
 4  This module provides a targeted fix for the NotImplementedError that occurs when
 5  creating subprocesses with asyncio on macOS with Python 3.11+.
 6  
 7  The issue occurs because the default event loop policy on macOS doesn't implement
 8  get_child_watcher(), which is required for subprocess creation.
 9  """
10  
11  import logging
12  import sys
13  
14  log = logging.getLogger(__name__)
15  
16  
17  def apply_macos_asyncio_fix() -> bool:
18      """
19      Apply minimal asyncio fix for macOS subprocess support.
20  
21      This fix specifically addresses the NotImplementedError in
22      asyncio.events.get_child_watcher() by providing a ThreadedChildWatcher
23      implementation that works on macOS.
24  
25      Returns:
26          bool: True if fix was applied or not needed, False if fix failed.
27      """
28      if sys.platform != "darwin" and sys.platform != "linux":
29          log.debug("Not on macOS or linux, asyncio fix not needed.")
30          return True
31  
32      import asyncio
33  
34      log.info(
35          "On macOS. Applying asyncio child watcher fix for Python %s",
36          sys.version.split()[0],
37      )
38  
39      try:
40          import asyncio.events
41          from asyncio.unix_events import ThreadedChildWatcher
42  
43          if not hasattr(asyncio.events, "_original_get_child_watcher_by_solace_fix"):
44              if hasattr(asyncio.events.get_child_watcher, "__name__") and (
45                  asyncio.events.get_child_watcher.__name__ == "get_child_watcher"
46                  or asyncio.events.get_child_watcher.__name__
47                  == "patched_get_child_watcher"
48              ):
49                  asyncio.events._original_get_child_watcher_by_solace_fix = (
50                      asyncio.events.get_child_watcher
51                  )
52  
53          def patched_get_child_watcher():
54              """Returns a ThreadedChildWatcher that works on macOS."""
55              return ThreadedChildWatcher()
56  
57          asyncio.events.get_child_watcher = patched_get_child_watcher
58  
59          test_watcher = asyncio.events.get_child_watcher()
60          log.info(
61              "Successfully applied asyncio fix. get_child_watcher is now patched to use %s.",
62              type(test_watcher).__name__,
63          )
64          return True
65  
66      except ImportError as e_imp:
67          log.error(
68              "Failed to import necessary asyncio modules for macOS fix: %s. This is unexpected.",
69              e_imp,
70          )
71          return False
72      except Exception as e:
73          log.error("Failed to apply unconditional asyncio fix for macOS: %s", e)
74          return False
75  
76  
77  def ensure_asyncio_compatibility():
78      """
79      Ensure asyncio compatibility for subprocess creation on macOS.
80      This function should be called as early as possible in the application.
81  
82      Returns:
83          bool: True if compatibility is ensured, False otherwise.
84      """
85      return apply_macos_asyncio_fix()
86  
87  
88  ensure_asyncio_compatibility()