@@ -65,50 +65,68 @@ async def start(self) -> None:
6565 raise RuntimeError (f'Failed to start Docker sandbox: { e } ' )
6666
6767 async def stop (self ) -> None :
68- """Stop the Docker container and clean up resources."""
68+ """Stop the Docker container without removing it unless configured.
69+
70+ When remove_on_exit is False, this method stops the container but keeps
71+ the container reference so get_execution_context() can return it.
72+ """
6973 if not self .container :
74+ self .update_status (SandboxStatus .STOPPED )
7075 return
7176
7277 try :
7378 self .update_status (SandboxStatus .STOPPING )
74- await self .cleanup ()
79+ await self .stop_container ()
80+
81+ # If configured to remove on exit, perform full cleanup (removes container and closes client)
82+ if self .config .remove_on_exit :
83+ await self .cleanup ()
84+
7585 self .update_status (SandboxStatus .STOPPED )
7686 except Exception as e :
7787 logger .error (f'Error stopping container: { e } ' )
7888 self .update_status (SandboxStatus .ERROR )
7989 raise
8090
8191 async def cleanup (self ) -> None :
82- """Clean up Docker resources."""
83- try :
84- # Stop and remove container
85- if self .container :
86- try :
87- # Stop container if running
88- self .container .reload ()
89- if self .container .status == 'running' :
90- self .container .stop (timeout = 10 )
91-
92- # Remove container if configured to do so
93- if self .config .remove_on_exit :
94- self .container .remove (force = True )
95- logger .debug (f'Container { self .container .id } removed' )
96- except Exception as e :
97- logger .error (f'Error cleaning up container: { e } ' )
98- finally :
99- self .container = None
100-
101- # Close Docker client
102- if self .client :
103- try :
104- self .client .close ()
105- except Exception as e :
106- logger .warning (f'Error closing Docker client: { e } ' )
107- finally :
108- self .client = None
92+ """Clean up Docker resources.
93+
94+ - Always stops the container if it is running.
95+ - Removes the container only when remove_on_exit is True.
96+ - Preserves container reference and client when remove_on_exit is False.
97+ """
98+ if self .container :
99+ try :
100+ self .container .remove (force = True )
101+ logger .debug (f'Container { self .container .id } removed' )
102+ except Exception as e :
103+ logger .error (f'Error cleaning up container: { e } ' )
104+ finally :
105+ # Only drop the reference when we actually removed it
106+ self .container = None
109107
108+ # Close Docker client only if we dropped the container reference
109+ if self .client :
110+ try :
111+ self .client .close ()
112+ except Exception as e :
113+ logger .warning (f'Error closing Docker client: { e } ' )
114+ finally :
115+ self .client = None
116+
117+ async def stop_container (self ) -> None :
118+ """Stop the container if it is running."""
119+ if not self .container :
120+ return
121+ try :
122+ self .container .reload ()
123+ if self .container .status == SandboxStatus .RUNNING :
124+ self .container .stop (timeout = 10 )
125+ except NotFound :
126+ logger .warning ('Container not found while stopping' )
110127 except Exception as e :
111- logger .error (f'Error during cleanup: { e } ' )
128+ logger .error (f'Error stopping container: { e } ' )
129+ raise
112130
113131 async def get_execution_context (self ) -> Any :
114132 """Return the container for tool execution."""
0 commit comments