From c403973616b4969f8f9c3bc48eae053da1d1a88e Mon Sep 17 00:00:00 2001 From: Graham Neubig Date: Wed, 4 Jun 2025 11:24:25 -0400 Subject: [PATCH] Add return type annotations to docker runtime (#8543) Co-authored-by: openhands --- .../runtime/impl/docker/docker_runtime.py | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/openhands/runtime/impl/docker/docker_runtime.py b/openhands/runtime/impl/docker/docker_runtime.py index 01e511a9ad..1176b19972 100644 --- a/openhands/runtime/impl/docker/docker_runtime.py +++ b/openhands/runtime/impl/docker/docker_runtime.py @@ -41,7 +41,7 @@ APP_PORT_RANGE_1 = (50000, 54999) APP_PORT_RANGE_2 = (55000, 59999) -def _is_retryablewait_until_alive_error(exception): +def _is_retryablewait_until_alive_error(exception: Exception) -> bool: if isinstance(exception, tenacity.RetryError): cause = exception.last_attempt.exception() return _is_retryablewait_until_alive_error(cause) @@ -140,10 +140,10 @@ class DockerRuntime(ActionExecutionClient): ) @property - def action_execution_server_url(self): + def action_execution_server_url(self) -> str: return self.api_url - async def connect(self): + async def connect(self) -> None: self.send_status_message('STATUS$STARTING_RUNTIME') try: await call_sync_from_async(self._attach_to_container) @@ -264,7 +264,7 @@ class DockerRuntime(ActionExecutionClient): return volumes - def init_container(self): + def init_container(self) -> None: self.log('debug', 'Preparing to start container...') self.send_status_message('STATUS$PREPARING_CONTAINER') self._host_port = self._find_available_port(EXECUTION_SERVER_PORT_RANGE) @@ -317,15 +317,18 @@ class DockerRuntime(ActionExecutionClient): ) # Combine environment variables - environment = { - 'port': str(self._container_port), - 'PYTHONUNBUFFERED': '1', - # Passing in the ports means nested runtimes do not come up with their own ports! - 'VSCODE_PORT': str(self._vscode_port), - 'APP_PORT_1': self._app_ports[0], - 'APP_PORT_2': self._app_ports[1], - 'PIP_BREAK_SYSTEM_PACKAGES': '1', - } + environment = dict(**self.initial_env_vars) + environment.update( + { + 'port': str(self._container_port), + 'PYTHONUNBUFFERED': '1', + # Passing in the ports means nested runtimes do not come up with their own ports! + 'VSCODE_PORT': str(self._vscode_port), + 'APP_PORT_1': str(self._app_ports[0]), + 'APP_PORT_2': str(self._app_ports[1]), + 'PIP_BREAK_SYSTEM_PACKAGES': '1', + } + ) if self.config.debug or DEBUG: environment['DEBUG'] = 'true' # also update with runtime_startup_env_vars @@ -396,7 +399,7 @@ class DockerRuntime(ActionExecutionClient): self.close() raise e - def _attach_to_container(self): + def _attach_to_container(self) -> None: self.container = self.docker_client.containers.get(self.container_name) if self.container.status == 'exited': self.container.start() @@ -432,7 +435,7 @@ class DockerRuntime(ActionExecutionClient): reraise=True, wait=tenacity.wait_fixed(2), ) - def wait_until_alive(self): + def wait_until_alive(self) -> None: try: container = self.docker_client.containers.get(self.container_name) if container.status == 'exited': @@ -446,7 +449,7 @@ class DockerRuntime(ActionExecutionClient): self.check_if_alive() - def close(self, rm_all_containers: bool | None = None): + def close(self, rm_all_containers: bool | None = None) -> None: """Closes the DockerRuntime and associated objects Parameters: @@ -466,7 +469,7 @@ class DockerRuntime(ActionExecutionClient): ) stop_all_containers(close_prefix) - def _is_port_in_use_docker(self, port): + def _is_port_in_use_docker(self, port: int) -> bool: containers = self.docker_client.containers.list() for container in containers: container_ports = container.ports @@ -474,7 +477,9 @@ class DockerRuntime(ActionExecutionClient): return True return False - def _find_available_port(self, port_range, max_attempts=5): + def _find_available_port( + self, port_range: tuple[int, int], max_attempts: int = 5 + ) -> int: port = port_range[1] for _ in range(max_attempts): port = find_available_tcp_port(port_range[0], port_range[1]) @@ -493,7 +498,7 @@ class DockerRuntime(ActionExecutionClient): return vscode_url @property - def web_hosts(self): + def web_hosts(self) -> dict[str, int]: hosts: dict[str, int] = {} host_addr = os.environ.get('DOCKER_HOST_ADDR', 'localhost') @@ -502,7 +507,7 @@ class DockerRuntime(ActionExecutionClient): return hosts - def pause(self): + def pause(self) -> None: """Pause the runtime by stopping the container. This is different from container.stop() as it ensures environment variables are properly preserved.""" if not self.container: @@ -515,7 +520,7 @@ class DockerRuntime(ActionExecutionClient): self.container.stop() self.log('debug', f'Container {self.container_name} paused') - def resume(self): + def resume(self) -> None: """Resume the runtime by starting the container. This is different from container.start() as it ensures environment variables are properly restored.""" if not self.container: @@ -529,7 +534,7 @@ class DockerRuntime(ActionExecutionClient): self.wait_until_alive() @classmethod - async def delete(cls, conversation_id: str): + async def delete(cls, conversation_id: str) -> None: docker_client = cls._init_docker_client() try: container_name = CONTAINER_NAME_PREFIX + conversation_id @@ -542,7 +547,7 @@ class DockerRuntime(ActionExecutionClient): finally: docker_client.close() - def get_action_execution_server_startup_command(self): + def get_action_execution_server_startup_command(self) -> list[str]: return get_action_execution_server_startup_command( server_port=self._container_port, plugins=self.plugins,