2020from databento .live import AUTH_TIMEOUT_SECONDS
2121from databento .live import CONNECT_TIMEOUT_SECONDS
2222from databento .live import DBNRecord
23+ from databento .live import ExceptionCallback
24+ from databento .live import RecordCallback
2325from databento .live .protocol import DatabentoLiveProtocol
2426
2527
2628logger = logging .getLogger (__name__ )
2729
2830
29- UserCallback = Callable [[DBNRecord ], None ]
3031DEFAULT_REMOTE_PORT = 13000
3132
3233
@@ -122,8 +123,8 @@ def __init__(
122123 api_key : str ,
123124 dataset : Dataset | str ,
124125 dbn_queue : DBNQueue ,
125- user_callbacks : list [ UserCallback ],
126- user_streams : list [IO [bytes ]],
126+ user_callbacks : dict [ RecordCallback , ExceptionCallback | None ],
127+ user_streams : dict [IO [bytes ], ExceptionCallback | None ],
127128 loop : asyncio .AbstractEventLoop ,
128129 metadata : SessionMetadata ,
129130 ts_out : bool = False ,
@@ -140,22 +141,28 @@ def __init__(
140141 def received_metadata (self , metadata : databento_dbn .Metadata ) -> None :
141142 if not self ._metadata :
142143 self ._metadata .data = metadata
143- for stream in self ._user_streams :
144- task = self ._loop .create_task (self ._stream_task (stream , metadata ))
144+ for stream , exc_callback in self ._user_streams .items ():
145+ task = self ._loop .create_task (
146+ self ._stream_task (stream , metadata , exc_callback ),
147+ )
145148 task .add_done_callback (self ._tasks .remove )
146149 self ._tasks .add (task )
147150 else :
148151 self ._metadata .check (metadata )
149152 return super ().received_metadata (metadata )
150153
151154 def received_record (self , record : DBNRecord ) -> None :
152- for callback in self ._user_callbacks :
153- task = self ._loop .create_task (self ._callback_task (callback , record ))
155+ for callback , exc_callback in self ._user_callbacks .items ():
156+ task = self ._loop .create_task (
157+ self ._callback_task (callback , record , exc_callback ),
158+ )
154159 task .add_done_callback (self ._tasks .remove )
155160 self ._tasks .add (task )
156161
157- for stream in self ._user_streams :
158- task = self ._loop .create_task (self ._stream_task (stream , record ))
162+ for stream , exc_callback in self ._user_streams .items ():
163+ task = self ._loop .create_task (
164+ self ._stream_task (stream , record , exc_callback ),
165+ )
159166 task .add_done_callback (self ._tasks .remove )
160167 self ._tasks .add (task )
161168
@@ -180,26 +187,29 @@ def received_record(self, record: DBNRecord) -> None:
180187
181188 async def _callback_task (
182189 self ,
183- func : UserCallback ,
190+ record_callback : RecordCallback ,
184191 record : DBNRecord ,
192+ exception_callback : ExceptionCallback | None ,
185193 ) -> None :
186194 try :
187- func (record )
195+ record_callback (record )
188196 except Exception as exc :
189197 logger .error (
190198 "error dispatching %s to `%s` callback" ,
191199 type (record ).__name__ ,
192- func .__name__ ,
200+ record_callback .__name__ ,
193201 exc_info = exc ,
194202 )
195- raise
203+ if exception_callback is not None :
204+ self ._loop .call_soon_threadsafe (exception_callback , exc )
196205
197206 async def _stream_task (
198207 self ,
199208 stream : IO [bytes ],
200209 record : databento_dbn .Metadata | DBNRecord ,
210+ exc_callback : ExceptionCallback | None ,
201211 ) -> None :
202- has_ts_out = self ._metadata and self ._metadata .data .ts_out
212+ has_ts_out = self ._metadata . data and self ._metadata .data .ts_out
203213 try :
204214 stream .write (bytes (record ))
205215 if not isinstance (record , databento_dbn .Metadata ) and has_ts_out :
@@ -212,7 +222,8 @@ async def _stream_task(
212222 stream_name ,
213223 exc_info = exc ,
214224 )
215- raise
225+ if exc_callback is not None :
226+ self ._loop .call_soon_threadsafe (exc_callback , exc )
216227
217228 async def wait_for_processing (self ) -> None :
218229 while self ._tasks :
0 commit comments