@@ -38,6 +38,10 @@ def model_to_dict(model: BaseModel) -> Dict[str, Any]:
3838 def serialize_item (item ):
3939 if isinstance (item , Enum ):
4040 return item .value .lower ()
41+ elif isinstance (item , BaseModel ):
42+ # Recursively serialize nested BaseModel instances with exclude_defaults=False
43+ nested_data = item .model_dump (exclude_none = True , exclude_defaults = False )
44+ return {key : serialize_item (value ) for key , value in nested_data .items ()}
4145 elif isinstance (item , dict ):
4246 return {key : serialize_item (value ) for key , value in item .items ()}
4347 elif isinstance (item , list ):
@@ -171,29 +175,51 @@ def wrapper(*args, **kwargs):
171175
172176def sync_wrapper (fn : Callable [[], Coroutine [Any , Any , Any ]]) -> Callable [[], None ]:
173177 def wrapper ():
178+ # Check if the interpreter is shutting down
179+ if sys is None or getattr (sys , "_getframe" , None ) is None :
180+ # Interpreter is shutting down, skip cleanup
181+ return
182+
174183 try :
175184 loop = asyncio .get_running_loop ()
176185 except RuntimeError :
177186 loop = None
187+ except Exception :
188+ # Any other exception during loop detection means we should skip cleanup
189+ return
190+
178191 try :
179192 if loop is None or not loop .is_running ():
193+ # Check if asyncio module is still available
194+ if asyncio is None :
195+ return
196+
180197 loop = asyncio .new_event_loop ()
181198 asyncio .set_event_loop (loop )
182199 task = loop .create_task (fn ())
183200 loop .run_until_complete (task )
184- except RuntimeError :
201+ except ( RuntimeError , AttributeError , TypeError ) as e :
185202 # This could happen if an object stored an event loop and now
186- # that event loop is closed. There's nothing we can do other than
187- # advise the user to use explicit cleanup methods .
203+ # that event loop is closed, or if asyncio modules are being
204+ # torn down during interpreter shutdown .
188205 #
189206 # Uses logging module instead of get_logger() to avoid I/O errors
190207 # if the wrapped function is called as a finalizer.
191- logging .info (
192- f"Could not run the async function { fn .__name__ } because the event loop is closed. "
193- "This usually means the object was not properly cleaned up. Please use explicit "
194- "cleanup methods (e.g., disconnect(), close()) or use the object as an async "
195- "context manager." ,
196- )
208+ if logging is not None :
209+ try :
210+ logging .info (
211+ f"Could not run the async function { fn .__name__ } because the event loop is closed "
212+ "or the interpreter is shutting down. "
213+ "This usually means the object was not properly cleaned up. Please use explicit "
214+ "cleanup methods (e.g., disconnect(), close()) or use the object as an async "
215+ "context manager." ,
216+ )
217+ except Exception :
218+ # Even logging failed, interpreter is really shutting down
219+ pass
220+ return
221+ except Exception :
222+ # Any other unexpected exception should be silently ignored during shutdown
197223 return
198224
199225 return wrapper
0 commit comments