From 601c2f6f258d77f4678f8cb49533ff745d53d1ec Mon Sep 17 00:00:00 2001 From: Graham Neubig Date: Tue, 6 May 2025 13:51:06 -0400 Subject: [PATCH] Add more extensive typing to openhands/runtime directory (#8288) Co-authored-by: openhands --- openhands/runtime/browser/browser_env.py | 17 +++++++++-------- openhands/runtime/builder/docker.py | 2 +- openhands/runtime/builder/remote.py | 4 ++-- openhands/runtime/file_viewer_server.py | 4 ++-- .../plugins/agent_skills/utils/dependency.py | 2 +- .../runtime/plugins/jupyter/execute_server.py | 2 +- openhands/runtime/utils/bash.py | 2 +- openhands/runtime/utils/git_handler.py | 2 +- openhands/runtime/utils/log_streamer.py | 2 +- openhands/runtime/utils/memory_monitor.py | 2 +- openhands/runtime/utils/request.py | 4 ++-- openhands/runtime/utils/runtime_build.py | 7 ++++--- openhands/runtime/utils/system_stats.py | 2 +- 13 files changed, 27 insertions(+), 25 deletions(-) diff --git a/openhands/runtime/browser/browser_env.py b/openhands/runtime/browser/browser_env.py index f12565341e..7880d52165 100644 --- a/openhands/runtime/browser/browser_env.py +++ b/openhands/runtime/browser/browser_env.py @@ -40,7 +40,7 @@ class BrowserEnv: self.init_browser() atexit.register(self.close) - def get_html_text_converter(self): + def get_html_text_converter(self) -> html2text.HTML2Text: html_text_converter = html2text.HTML2Text() # ignore links and images html_text_converter.ignore_links = False @@ -56,7 +56,7 @@ class BrowserEnv: stop=tenacity.stop_after_attempt(5) | stop_if_should_exit(), retry=tenacity.retry_if_exception_type(BrowserInitException), ) - def init_browser(self): + def init_browser(self) -> None: logger.debug('Starting browser env...') try: self.process = multiprocessing.Process(target=self.browser_process) @@ -69,7 +69,7 @@ class BrowserEnv: self.close() raise BrowserInitException('Failed to start browser environment.') - def browser_process(self): + def browser_process(self) -> None: if self.eval_mode: assert self.browsergym_eval_env is not None logger.info('Initializing browser env for web browsing evaluation.') @@ -196,17 +196,18 @@ class BrowserEnv: if self.agent_side.poll(timeout=0.01): response_id, obs = self.agent_side.recv() if response_id == unique_request_id: - return obs + return dict(obs) - def check_alive(self, timeout: float = 60): + def check_alive(self, timeout: float = 60) -> bool: self.agent_side.send(('IS_ALIVE', None)) if self.agent_side.poll(timeout=timeout): response_id, _ = self.agent_side.recv() if response_id == 'ALIVE': return True logger.debug(f'Browser env is not alive. Response ID: {response_id}') + return False - def close(self): + def close(self) -> None: if not self.process.is_alive(): return try: @@ -229,7 +230,7 @@ class BrowserEnv: @staticmethod def image_to_png_base64_url( image: np.ndarray | Image.Image, add_data_prefix: bool = False - ): + ) -> str: """Convert a numpy array to a base64 encoded png image url.""" if isinstance(image, np.ndarray): image = Image.fromarray(image) @@ -248,7 +249,7 @@ class BrowserEnv: @staticmethod def image_to_jpg_base64_url( image: np.ndarray | Image.Image, add_data_prefix: bool = False - ): + ) -> str: """Convert a numpy array to a base64 encoded jpeg image url.""" if isinstance(image, np.ndarray): image = Image.fromarray(image) diff --git a/openhands/runtime/builder/docker.py b/openhands/runtime/builder/docker.py index 618fe913c0..d599673698 100644 --- a/openhands/runtime/builder/docker.py +++ b/openhands/runtime/builder/docker.py @@ -36,7 +36,7 @@ class DockerRuntimeBuilder(RuntimeBuilder): self.rolling_logger = RollingLogger(max_lines=10) @staticmethod - def check_buildx(is_podman: bool = False): + def check_buildx(is_podman: bool = False) -> bool: """Check if Docker Buildx is available""" try: result = subprocess.run( diff --git a/openhands/runtime/builder/remote.py b/openhands/runtime/builder/remote.py index 37c236f045..c665ced5f0 100644 --- a/openhands/runtime/builder/remote.py +++ b/openhands/runtime/builder/remote.py @@ -100,7 +100,7 @@ class RemoteRuntimeBuilder(RuntimeBuilder): if status == 'SUCCESS': logger.debug(f"Successfully built {status_data['image']}") - return status_data['image'] + return str(status_data['image']) elif status in [ 'FAILURE', 'INTERNAL_ERROR', @@ -146,4 +146,4 @@ class RemoteRuntimeBuilder(RuntimeBuilder): else: logger.debug(f'Image {image_name} does not exist.') - return result['exists'] + return bool(result['exists']) diff --git a/openhands/runtime/file_viewer_server.py b/openhands/runtime/file_viewer_server.py index f045a85467..d0d3c88e0d 100644 --- a/openhands/runtime/file_viewer_server.py +++ b/openhands/runtime/file_viewer_server.py @@ -21,12 +21,12 @@ def create_app() -> FastAPI: ) @app.get('/') - async def root(): + async def root() -> dict[str, str]: """Root endpoint to check if the server is running.""" return {'status': 'File viewer server is running'} @app.get('/view') - async def view_file(path: str, request: Request): + async def view_file(path: str, request: Request) -> HTMLResponse: """View a file using an embedded viewer. Args: diff --git a/openhands/runtime/plugins/agent_skills/utils/dependency.py b/openhands/runtime/plugins/agent_skills/utils/dependency.py index 1ff1636fb5..6143bd93e8 100644 --- a/openhands/runtime/plugins/agent_skills/utils/dependency.py +++ b/openhands/runtime/plugins/agent_skills/utils/dependency.py @@ -2,7 +2,7 @@ from types import ModuleType def import_functions( - module: ModuleType, function_names: list[str], target_globals: dict + module: ModuleType, function_names: list[str], target_globals: dict[str, object] ) -> None: for name in function_names: if hasattr(module, name): diff --git a/openhands/runtime/plugins/jupyter/execute_server.py b/openhands/runtime/plugins/jupyter/execute_server.py index af2f554d03..af63b6d39d 100644 --- a/openhands/runtime/plugins/jupyter/execute_server.py +++ b/openhands/runtime/plugins/jupyter/execute_server.py @@ -138,7 +138,7 @@ class JupyterKernel: retry=retry_if_exception_type(ConnectionRefusedError), stop=stop_after_attempt(3), wait=wait_fixed(2), - ) + ) # type: ignore async def execute(self, code: str, timeout: int = 120) -> str: if not self.ws or self.ws.stream.closed(): await self._connect() diff --git a/openhands/runtime/utils/bash.py b/openhands/runtime/utils/bash.py index fec9f92170..da4ddd22c4 100644 --- a/openhands/runtime/utils/bash.py +++ b/openhands/runtime/utils/bash.py @@ -6,7 +6,7 @@ import uuid from enum import Enum from typing import Any -import bashlex # type: ignore +import bashlex import libtmux from openhands.core.logger import openhands_logger as logger diff --git a/openhands/runtime/utils/git_handler.py b/openhands/runtime/utils/git_handler.py index 7749e1f5f7..6dc20f6d11 100644 --- a/openhands/runtime/utils/git_handler.py +++ b/openhands/runtime/utils/git_handler.py @@ -28,7 +28,7 @@ class GitHandler: self.execute = execute_shell_fn self.cwd: str | None = None - def set_cwd(self, cwd: str): + def set_cwd(self, cwd: str) -> None: """ Sets the current working directory for Git operations. diff --git a/openhands/runtime/utils/log_streamer.py b/openhands/runtime/utils/log_streamer.py index 0c9e46b845..022b85dfdb 100644 --- a/openhands/runtime/utils/log_streamer.py +++ b/openhands/runtime/utils/log_streamer.py @@ -14,7 +14,7 @@ class LogStreamer: def __init__( self, container: docker.models.containers.Container, - logFn: Callable, + logFn: Callable[[str, str], None], ): self.log = logFn # Initialize all attributes before starting the thread on this instance diff --git a/openhands/runtime/utils/memory_monitor.py b/openhands/runtime/utils/memory_monitor.py index ea6d923161..fe1209f751 100644 --- a/openhands/runtime/utils/memory_monitor.py +++ b/openhands/runtime/utils/memory_monitor.py @@ -2,7 +2,7 @@ import threading -from memory_profiler import memory_usage # type: ignore +from memory_profiler import memory_usage from openhands.core.logger import openhands_logger as logger diff --git a/openhands/runtime/utils/request.py b/openhands/runtime/utils/request.py index 5a37a0d049..96866f5a7b 100644 --- a/openhands/runtime/utils/request.py +++ b/openhands/runtime/utils/request.py @@ -19,7 +19,7 @@ class RequestHTTPError(httpx.HTTPStatusError): s = super().__str__() if self.detail is not None: s += f'\nDetails: {self.detail}' - return s + return str(s) def is_retryable_error(exception: Any) -> bool: @@ -57,4 +57,4 @@ def send_request( response=e.response, detail=_json.get('detail') if _json is not None else None, ) from e - return response # type: ignore + return response diff --git a/openhands/runtime/utils/runtime_build.py b/openhands/runtime/utils/runtime_build.py index 799bdeafff..7ee10f537a 100644 --- a/openhands/runtime/utils/runtime_build.py +++ b/openhands/runtime/utils/runtime_build.py @@ -8,7 +8,7 @@ from enum import Enum from pathlib import Path import docker -from dirhash import dirhash # type: ignore +from dirhash import dirhash from jinja2 import Environment, FileSystemLoader import openhands @@ -283,8 +283,9 @@ def prep_build_folder( build_from=build_from, extra_deps=extra_deps, ) - with open(Path(build_folder, 'Dockerfile'), 'w') as file: # type: ignore - file.write(dockerfile_content) # type: ignore + dockerfile_path = Path(build_folder, 'Dockerfile') + with open(str(dockerfile_path), 'w') as f: + f.write(dockerfile_content) _ALPHABET = string.digits + string.ascii_lowercase diff --git a/openhands/runtime/utils/system_stats.py b/openhands/runtime/utils/system_stats.py index d0068c2487..b34738d48e 100644 --- a/openhands/runtime/utils/system_stats.py +++ b/openhands/runtime/utils/system_stats.py @@ -5,7 +5,7 @@ import time import psutil -def get_system_stats() -> dict: +def get_system_stats() -> dict[str, object]: """Get current system resource statistics. Returns: