Skip to content

Fix Deno versioning and isolate PythonInterpreter dependencies - #10186

Merged
isaacbmiller merged 5 commits into
mainfrom
fix/hermetic-deno-runtime
Aug 12, 2026
Merged

isaacbmiller merged 5 commits into
mainfrom
fix/hermetic-deno-runtime

Conversation

@isaacbmiller

@isaacbmiller isaacbmiller commented Aug 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • add an optional dspy[deno] extra backed by Deno's official PyPI binary distribution (>=2.4.5,<3)
  • prefer the Python-managed binary while preserving system deno and explicit deno_command fallbacks
  • support system Deno across the full 2.x line (>=2.0.0,<3)
  • isolate the default runner from ambient package.json, deno.json, lockfiles, and local node_modules
  • pin the runner's Pyodide dependency to 0.29.4
  • bound Deno version/cache probes to 10 seconds and revalidate the executable on each process launch
  • exercise both the minimum and latest managed Deno dependency resolutions in CI

Root cause

PythonInterpreter launched an absolute runner.js path but inherited the caller's working directory and Deno project discovery. When the cwd or an ancestor contained package.json and node_modules, Deno could resolve the unpinned npm:pyodide import through that project. Those files were outside DSPy's read allowlist, causing startup to fail with NotCapable, followed by an unresolved top-level await.

The default invocation is now hermetic: DSPy's generated Deno subprocesses receive DENO_NO_PACKAGE_JSON=1, and the runner is launched with --no-config, --no-lock, and --node-modules-dir=false. The same resolved executable is used for version detection, deno info, and execution.

Compatibility

  • pip install dspy continues to use a compatible Deno 2.x runtime on PATH.
  • pip install "dspy[deno]" installs and locks the managed runtime from the earliest official stable PyPI package (2.4.5) through the latest 2.x release.
  • System Deno >=2.0.0,<3.0.0 is accepted; the minimum was verified against the real Deno 2.0.0 binary rather than inherited from the previous CI pin.
  • An explicit deno_command remains unchanged, retains its caller-controlled environment, and bypasses default version validation.
  • Release smoke tests retain setup-deno to cover the existing PATH-based installation flow.
  • Default managed/system execution reports a clear error when the runtime version cannot be determined or is outside the supported major-version range.
  • deno --version and deno info --json are bounded to 10 seconds; version results are not cached across separate process launches.

Validation

  • uv run pytest tests/primitives/test_python_interpreter.py --deno -q — 91 passed
  • hostile parent project containing package.json, package-lock.json, deno.json, corrupt deno.lock, and fake node_modules/pyodide — passed without project-tree mutations
  • real Deno 2.0.0, 2.4.5, 2.6.10, 2.7.7, and 2.9.5 execution — passed
  • clean PATH-only Python environment with no deno Python package — passed
  • managed runtime precedence over an older PATH runtime — passed
  • timeout injection for both Deno probes and in-place executable replacement — passed
  • dependency resolution: lowest-direct selects 2.4.5; highest selects 2.9.5
  • uv lock --check, Ruff, and git diff --check — passed
  • built wheel contains runner.js and advertises the managed Deno range

@greptile-apps

greptile-apps Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This change adds a managed Deno runtime option and makes generated PythonInterpreter commands hermetic. Runtime checks confirmed that explicit custom Deno commands preserve DENO_NO_PACKAGE_JSON=0, invalid Deno version probes stop startup before a runner launches, and a poisoned parent Node project cannot redirect Pyodide resolution or create a parent lockfile.

Confidence Score: 5/5

No blocking failure remains.

The exercised custom-command, invalid-runtime, and ambient-resolution paths all produced the intended behavior.

T-Rex T-Rex Logs

What T-Rex did

  • Executed a runtime harness that constructed `PythonInterpreter(deno_command=[...])`, set `DENO_NO_PACKAGE_JSON=0`, intercepted `subprocess.Popen`, and called `_spawn_process`.
  • The harness observed `caller_DENO_NO_PACKAGE_JSON=0`, `popen_DENO_NO_PACKAGE_JSON=0`, and `environment_copy=True` with exit code 0.
  • This confirms the explicit custom-command path passes a copy of the caller environment without overriding the setting.
  • Executed a focused runtime harness through `PythonInterpreter._spawn_process` with malformed stdout, stderr-only version output, and a nonzero `deno --version` result.
  • Each case raised `CodeInterpreterError`, ran exactly one version probe, and made zero `Popen` calls; the repository's focused parametrized pytest also passed all three cases.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (4): Last reviewed commit: "fix: support all Deno 2 runtimes" | Re-trigger Greptile

Comment thread dspy/primitives/python_interpreter.py Outdated
Comment thread dspy/primitives/python_interpreter.py Outdated
@michaelisaac-dev

Copy link
Copy Markdown
Collaborator

LGTM

@isaacbmiller
isaacbmiller merged commit 07a0f7a into main Aug 12, 2026
24 checks passed
@isaacbmiller
isaacbmiller deleted the fix/hermetic-deno-runtime branch August 12, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants