From 82bb54de085e4e43e9c0d4bba77a141d794e8373 Mon Sep 17 00:00:00 2001 From: Nissan Pow Date: Sat, 9 May 2026 22:24:54 -0700 Subject: [PATCH] fix(ci): unblock mypy on backend modules + 4 strict-mode errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI's `mypy src/sandrun` step has been red since the strict-typing configuration landed. Most failures (44 errors) are 3rd-party SDK imports without stubs — `e2b_code_interpreter`, `daytona`, `boxlite`, `boxlite.sync_api`, `e2b.sandbox.commands.command_handle`. mypy types those imports as `object` and then complains about every attribute access (`.commands`, `.files`, `.process`, `.fs`, `_box`, `_runtime`, `exec`, etc.) inside the backend wrapper modules. Fix: pyproject [tool.mypy.overrides] - ignore_missing_imports for the five 3rd-party SDK modules - per-file: relax disallow_untyped_defs / warn_return_any + disable attr-defined / no-any-return on the three backend modules that wrap the SDKs (sandrun.backends.{e2b,daytona,boxlite}). Each backend has its own integration test against the real SDK; static noise was masking real issues elsewhere. Plus four pre-existing strict-mode errors that were getting buried under the 40+ backend errors: - backends/__init__.py:48 — `return cls()` widened to Any. Bind to a typed local first (`instance: SandboxBackend = cls()`). - _micromamba.py:151,156 — `path` was inferred as `str` from the earlier `if explicit:` branch and rejected the later `shutil.which(...)` (`str | None`) assignment. Add explicit `path: str | None` annotation at function scope. - installer.py:364 — `inst._script_path = script_path` set an attribute that `__init__` never declared. Add a class-level `_script_path: str | None = None` default with a comment explaining it's set by `_from_pep723_path()` for the inline-metadata path. Local: ruff check + ruff format --check + mypy + 92 pytests all green. Co-Authored-By: Claude Opus 4.7 (1M context) --- pyproject.toml | 27 +++++++++++++++++++++++++++ src/sandrun/_micromamba.py | 1 + src/sandrun/backends/__init__.py | 3 ++- src/sandrun/installer.py | 4 ++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f08760d..d00d236 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,3 +81,30 @@ python_version = "3.9" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true + +# The backend modules import optional 3rd-party sandbox SDKs (`e2b_code_interpreter`, +# `daytona`, `boxlite`) that don't ship type stubs. Without overrides, mypy types +# those imports as `object` and then flags every attribute access on the SDK +# clients. Each backend is exercised by its own integration tests against the +# real SDK; suppress the missing-stub noise so static checks don't block CI. +[[tool.mypy.overrides]] +module = [ + "e2b_code_interpreter", + "e2b.sandbox.commands.command_handle", + "daytona", + "boxlite", + "boxlite.sync_api", +] +ignore_missing_imports = true + +# The backend modules consume the SDKs above as untyped Any. disallow_untyped_defs +# + warn_return_any cascade into noise. Disable strict checks for these files only. +[[tool.mypy.overrides]] +module = [ + "sandrun.backends.e2b", + "sandrun.backends.daytona", + "sandrun.backends.boxlite", +] +disallow_untyped_defs = false +warn_return_any = false +disable_error_code = ["attr-defined", "no-any-return"] diff --git a/src/sandrun/_micromamba.py b/src/sandrun/_micromamba.py index 66088a6..aa80ed8 100644 --- a/src/sandrun/_micromamba.py +++ b/src/sandrun/_micromamba.py @@ -143,6 +143,7 @@ def resolve_micromamba() -> tuple[str | None, bool]: explicit = os.environ.get("SANDRUN_MICROMAMBA_PATH") or os.environ.get( "METAFLOW_SANDBOX_MICROMAMBA_PATH" ) + path: str | None if explicit: path = explicit compatible = is_compatible_linux_micromamba(path) diff --git a/src/sandrun/backends/__init__.py b/src/sandrun/backends/__init__.py index b25723a..84eaccd 100644 --- a/src/sandrun/backends/__init__.py +++ b/src/sandrun/backends/__init__.py @@ -45,4 +45,5 @@ def get_backend(name: str) -> SandboxBackend: module = importlib.import_module(module_path, package=__name__) cls = getattr(module, class_name) - return cls() + instance: SandboxBackend = cls() + return instance diff --git a/src/sandrun/installer.py b/src/sandrun/installer.py index d3f3813..2f227c4 100644 --- a/src/sandrun/installer.py +++ b/src/sandrun/installer.py @@ -336,6 +336,10 @@ class UvDepInstaller(DepInstaller): _MANIFEST_NAME = "sandrun-uv-manifest.txt" + # Set by _from_pep723_path() when the installer is built from an inline- + # metadata PEP 723 script. None for the regular requirements-list flow. + _script_path: str | None = None + def __init__(self, staging_dir: str | None = None) -> None: if staging_dir is None: self._staging_dir = tempfile.mkdtemp(prefix="sandrun-uv-deps-")