Add ctx.packages namespace to code mode#9233
Conversation
Code mode's single `install_packages()` method dispatched to
`InstallPackagesCommand`, which scans the kernel for all other missing
modules and re-runs affected cells on success. That's the right default
for auto-install on import error, but surprising when an agent is
explicitly asking to add one package.
These changes introduces a `ctx.packages` namespace with `add`,
`remove`, and `list`, installed into `marimo/_code_mode/_packages.py`.
`add` goes through a custom install loop that keeps the streaming
frontend notifications (queued → installing → installed/failed with
logs) but drops the missing-module scan and cell re-runs (installing
exactly what was asked, nothing more). `remove` and `list` are thin
wrappers over `PackageManager.uninstall` / `list_packages` and update
the script metadata for sandboxed notebooks the same way the HTTP
endpoints do.
Mutations are queued and flushed on context exit before cell ops, so
newly installed packages are importable from cells added in the same
batch. `list` returns the current environment state and raises if `add`
or `remove` have been queued. The alternative (stale data or silently
overlaying pending ops) is worse than a hard error.
async with cm.get_context() as ctx:
ctx.packages.list()
ctx.packages.add("pandas", "numpy>=1.26")
ctx.packages.remove("old-pkg")
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Introduces an explicit ctx.packages namespace in code mode to manage Python packages in a queued/atomic way, avoiding the surprising “scan for other missing modules + rerun cells” behavior when a caller asks to install a specific package.
Changes:
- Added
marimo/_code_mode/_packages.pyimplementing queuedadd,remove, andlistoperations with streaming install notifications and optional script-metadata updates. - Integrated package-op flushing into
AsyncCodeModeContext.__aexit__(flushed before cell operations) and addedAsyncCodeModeContext.packagesproperty. - Updated code-mode context tests to cover
ctx.packagesbehavior (queueing, flushing, list semantics, summary output, etc.).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
tests/_code_mode/test_context.py |
Replaces install_packages() tests with comprehensive coverage for ctx.packages.{add,remove,list}. |
marimo/_code_mode/_packages.py |
New implementation of the queued package-ops API with notifications + script-metadata updates. |
marimo/_code_mode/_context.py |
Wires Packages into code-mode context lifecycle and summary output; removes install_packages(). |
| ctx.packages.remove("old-pkg") | ||
| """ | ||
| return self._packages | ||
|
|
There was a problem hiding this comment.
install_packages() was removed from AsyncCodeModeContext, but the repo still references it (e.g. scratchpad import-error hint + its test expect ctx.install_packages). This will break those call sites and likely fail the existing test suite. Consider keeping install_packages() as a backwards-compatible alias that delegates to ctx.packages.add(...) (possibly deprecated), or update all remaining references in the same PR.
| def install_packages(self, *packages: str) -> None: | |
| """Backwards-compatible alias for ``ctx.packages.add(...)``. | |
| This preserves older internal call sites that still use | |
| ``ctx.install_packages(...)`` after package management moved to | |
| the ``packages`` accessor. | |
| """ | |
| self.packages.add(*packages) |
|
|
||
| async def test_remove_single(self, k: Kernel) -> None: |
There was a problem hiding this comment.
test_add_queued_before_cell_ops only asserts that install was called, not that it happened before the queued cell operations were applied. To actually verify the ordering guarantee, consider tracking both the package install and the kernel cell-execution (e.g., patch the kernel run/execute path) and assert the relative call sequence.
|
|
||
| ops = self._ops | ||
| self._ops = [] | ||
|
|
There was a problem hiding this comment.
The _flush() docstring says it "Returns the ops that ran", but the method can return ops that were never executed (e.g., when there’s no package manager or it isn’t installed). Either adjust the docstring to reflect "ops that were queued/attempted" or change the return value to only include successfully executed ops.
| return ops | ||
|
|
||
| source: Literal["kernel", "server"] = "kernel" | ||
| statuses: PackageStatusType = {} | ||
| for op in ops: |
There was a problem hiding this comment.
When package_manager is None or the manager binary is missing, _flush() returns ops even though nothing was executed. Since AsyncCodeModeContext uses this return value for the printed summary, this can result in misleading "installed/uninstalled ..." output. Consider returning an empty list here (or returning a separate "attempted" list) so summaries reflect what actually happened.
Pending on marimo-team/marimo#9233 The code mode API for package installation changed from a single `ctx.install_packages()` method to a `ctx.packages` namespace, with `add()` as the install entry point. This brings the docs in line with the current API so examples actually run, and leaves room for related package operations (remove, list) to live under the same namespace. `ctx.install_packages()` still will work (but with deprecation notice).
Pending on marimo-team/marimo#9233 The code mode API for package installation changed from a single `ctx.install_packages()` method to a `ctx.packages` namespace, with `add()` as the install entry point. This brings the docs in line with the current API so examples actually run, and leaves room for related package operations (remove, list) to live under the same namespace. `ctx.install_packages()` still will work (but with deprecation notice).
The previous commit replaced `ctx.install_packages()` with `ctx.packages.add()`, but existing skills and example snippets still reach for the old name. Rather than churn those, this adds a `__getattr__` shim on `AsyncCodeModeContext` that transparently forwards `install_packages` to `packages.add`. `__getattr__` only fires when normal attribute lookup fails, so the name doesn't show up in `dir()` or IDE completion — the new `ctx.packages` namespace remains the discoverable API surface while the legacy call path keeps working silently. Easy to delete once all callers migrate.
40b683e to
3fb4763
Compare
| # API. Kept as a hidden shim for in-flight skills / examples; | ||
| # does not appear in dir() or IDE completion. Prefer | ||
| # `ctx.packages.add(...)` in new code. | ||
| if name == "install_packages": |
There was a problem hiding this comment.
ctx.install_packages is referenced in the skill, so we should probably keep around for a bit: marimo-team/marimo-pair#36
Could use as a hook/nudge to prompt the user to upgrade the skill.
| return result | ||
|
|
||
|
|
||
| class Packages: |
There was a problem hiding this comment.
probably worth adding the help class here
|
🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.2-dev47 |
Code mode's single
install_packages()method dispatched toInstallPackagesCommand, which scans the kernel for all other missing modules and re-runs affected cells on success. That's the right default for auto-install on import error, but surprising when an agent is explicitly asking to add one package.These changes introduces a
ctx.packagesnamespace withadd,remove, andlist, installed intomarimo/_code_mode/_packages.py.addgoes through a custom install loop that keeps the streaming frontend notifications (queued → installing → installed/failed with logs) but drops the missing-module scan and cell re-runs (installing exactly what was asked, nothing more).removeandlistare thin wrappers overPackageManager.uninstall/list_packagesand update the script metadata for sandboxed notebooks the same way the HTTP endpoints do.Mutations are queued and flushed on context exit before cell ops, so newly installed packages are importable from cells added in the same batch.
listreturns the current environment state and raises ifaddorremovehave been queued. The alternative (stale data or silently overlaying pending ops) is worse than a hard error.