MCPcopy Create free account
hub / github.com/FastLED/FastLED / FbuildSerialAdapter

Class FbuildSerialAdapter

ci/util/serial_interface.py:130–240  ·  view source on GitHub ↗

Adapter wrapping fbuild SerialMonitor as SerialInterface. fbuild's SerialMonitor uses run_until_complete() internally, which crashes when called from an already-running async event loop. This adapter offloads all sync calls to a ThreadPoolExecutor to avoid the conflict.

Source from the content-addressed store, hash-verified

128
129
130class FbuildSerialAdapter:
131 """Adapter wrapping fbuild SerialMonitor as SerialInterface.
132
133 fbuild's SerialMonitor uses run_until_complete() internally, which crashes
134 when called from an already-running async event loop. This adapter offloads
135 all sync calls to a ThreadPoolExecutor to avoid the conflict.
136 """
137
138 _warned: bool = False
139
140 def __init__(
141 self,
142 port: str,
143 baud_rate: int = 115200,
144 auto_reconnect: bool = True,
145 verbose: bool = False,
146 ) -> None:
147 from fbuild.api import SerialMonitor
148
149 self._monitor = SerialMonitor(
150 port=port,
151 baud_rate=baud_rate,
152 auto_reconnect=auto_reconnect,
153 verbose=verbose,
154 )
155 self._executor = ThreadPoolExecutor(max_workers=1)
156
157 def _warn_once(self) -> None:
158 if not FbuildSerialAdapter._warned:
159 FbuildSerialAdapter._warned = True
160 warnings.warn(
161 "Converting sync serial monitor (fbuild) to async via thread "
162 "executor. This will be removed when fbuild provides native "
163 "async support.",
164 stacklevel=3,
165 )
166
167 async def _run_in_thread(self, func, *args): # type: ignore[no-untyped-def]
168 self._warn_once()
169 loop = asyncio.get_running_loop()
170 return await loop.run_in_executor(self._executor, func, *args)
171
172 async def connect(self) -> None:
173 await self._run_in_thread(self._monitor.__enter__)
174
175 async def close(self) -> None:
176 await self._run_in_thread(self._monitor.__exit__, None, None, None)
177 # NOTE: do NOT shut down self._executor here. The autoresearch
178 # recovery path (ci/autoresearch/gpio.py) calls close() then reuses
179 # the same adapter on a new RpcClient. Shutting the executor down
180 # makes the next _run_in_thread fail with "cannot schedule new
181 # futures after shutdown" and breaks DTR-reset retries. See
182 # FastLED/fbuild#592.
183
184 async def write(self, data: str) -> None:
185 await self._run_in_thread(self._monitor.write, data)
186
187 async def read_lines(self, timeout: float) -> AsyncIterator[str]:

Callers 1

create_serial_interfaceFunction · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected