Skip to content

feat: Support PEP 723 scripts with --script - #6648

Merged
ruben-arts merged 28 commits into
prefix-dev:mainfrom
manzt:manzt/pep-723-conda-exec
Jul 30, 2026
Merged

ruben-arts merged 28 commits into
prefix-dev:mainfrom
manzt:manzt/pep-723-conda-exec

Conversation

@manzt

@manzt manzt commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

This PR follows the discussion in #6632 and reworks the earlier manzt/pep-723 prototype.

Edited This PR originally added a pixi script command and conda-exec support. After review, it now uses --script with existing Pixi commands and supports PEP 723 with tool.pixi. This leaves pixi script open for other uses later.


This PR adds support for standalone Python files with inline environment metadata.

pixi run --script example.py

Pixi creates a cached environment for the script. It does not use an enclosing workspace or activated environment.

PEP 723 declares the Python version and PyPI dependencies. Pixi also reads tool.pixi for Conda packages and settings that PEP 723 cannot express.

# /// script
# requires-python = ">=3.11"
# dependencies = ["httpx"]
#
# [tool.pixi.workspace]
# channels = ["conda-forge"]
#
# [tool.pixi.dependencies]
# gdal = "*"
# ///

Other PEP 723 tools can use the standard fields. Pixi can also install the Conda dependencies.

Scripts use the existing Pixi commands.

pixi init --script example.py
pixi run --script example.py
pixi add --script example.py requests
pixi add --script example.py --pypi httpx
pixi remove --script example.py requests
pixi lock --script example.py

--script is also available for tree and the relevant workspace channel, workspace platform, and workspace export commands.

Where both are present, --script and --manifest-path cannot be used together. --script is only added to commands that have useful behavior for a standalone script. It is not available for commands such as install, shell, task, or workspace configuration.

Only pixi init --script creates a file or adds a PEP 723 block. Other commands require the metadata to already exist.

Without a lockfile, Pixi resolves the environment without writing one. pixi lock --script writes example.py.pixi.lock next to the script. Later commands reuse it. The environment itself stays in the Pixi cache.

This PR does not add tool.conda, conda-exec support, or a new Pixi metadata format.

See the standalone scripts documentation for more examples.

@manzt
manzt marked this pull request as draft July 22, 2026 19:11
@manzt manzt changed the title feat: add standalone PEP 723 script workflows Add pixi script for standalone Python scripts Jul 22, 2026
@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch from 8c7125c to 8242e11 Compare July 22, 2026 20:46
@manzt manzt changed the title Add pixi script for standalone Python scripts feat: Add pixi script for standalone Python scripts Jul 22, 2026
@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch 2 times, most recently from 85ad787 to 5b861b7 Compare July 22, 2026 22:10
@manzt
manzt marked this pull request as ready for review July 22, 2026 22:13
@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch 3 times, most recently from 67f32b3 to 0537df3 Compare July 23, 2026 17:13
@manzt

manzt commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Apologies for all the noise re: referenced PRs. I got early access to GitHub stacked PRs and was trying to see if I could use to make this easier for review / have relevant discussions for each piece. The individual commits here are the same as the stack.

@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch from 0537df3 to 8b58c6c Compare July 23, 2026 18:29
@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch from 8b58c6c to 3b68b23 Compare July 25, 2026 16:04
@ruben-arts

Copy link
Copy Markdown
Contributor

I've started to take a look at how this works. I'm happy that the pep just works.

Now that I'm trying it I was searching for examples where people use [tool.conda] and I found that the conda-exec tools is not really relevant yet. And due to a total mismatch in configuration I'm now not sure we should actually support this. The Pixi spec allows to basically redo what is already supported in the pyproject.toml with pixi and it's relevant enough that I would say it makes more sense than the conda-exec spec.

WDYT? @manzt @Hofer-Julian @baszalmstra

@Hofer-Julian

Copy link
Copy Markdown
Member

The Pixi spec allows to basically redo what is already supported in the pyproject.toml with pixi and it's relevant enough that I would say it makes more sense than the conda-exec spec.

Fully agreed.

Let's also make sense to have a good story for the CLI.
pixi script should for sure be used for #3751, so let's make sure it will not be reserved for PEP support.

See also my comment here: #6632 (comment)

@manzt

manzt commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Let's also make sense to have a good story for the CLI. pixi script should for sure be used for #3751, so let's make sure it will not be reserved for PEP support.

Agreed. I see pixi script as the general CLI namespace for standalone scripts, not something exclusive to PEP 723 or Python. For Python, though, I would strongly encourage PEP 723 as the default, first-class representation.

Its interoperability would be especially valuable for us at marimo, so users can share/execute standalone notebooks potentially authored by either uv or pixi. For instance, many of our learning materials are PEP 723 notebooks (authored with uv) which we could now point users to run on their own with whatever CLI they have installed.

I see the formats as roughly analogous:

Workspace Standalone script
pyproject.toml PEP 723 # /// script
pixi.toml A future Pixi-native # /// pixi-script block

In principle, even a Python file could even support script OR pixi-script metadata. That said, I'd like to see Pixi defaulting to creating PEP 723 metadata for Python, while a future pixi-script block could provide Pixi-native configuration shared with other languages. I don't see anything in the current command design that prevents layering this in later.

We could do a hard check for # /// script right now, leaving pixi-script support for future work.

Due to a total mismatch in configuration I'm now not sure we should actually support this.

Agreed on dropping tool.conda as well. That simplifies both the metadata model and the implementation. Unless there are objections, I can remove it here, clarify the broader direction for pixi script, and leave the native pixi-script format for future work.

@Hofer-Julian

Copy link
Copy Markdown
Member

That said, I'd like to see Pixi defaulting to creating PEP 723 metadata for Python, while a future pixi-script block could provide Pixi-native configuration shared with other languages. I don't see anything in the current command design that prevents layering this in later.

I don't really understand what you mean with defaulting here. If we simply check whether it's script or pixi-script then there will be neither ambiguity nor defaulting. Or am I missing something?

@manzt

manzt commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

The pixi script init creates the metadata block.

@Hofer-Julian

Hofer-Julian commented Jul 28, 2026 •

Copy link
Copy Markdown
Member

The pixi script init creates the metadata block.

Then I disagree that this would default to PEP style :)

But we also don't have to decide on that now. I would really appreciate it if you would trim this PR down to its bare minimum. It only needs to support what uv run does and add a bit of nice documentation. No pixi script init, no conda-script, no [tool.pixi] support.

@Hofer-Julian

Hofer-Julian commented Jul 28, 2026 •

Copy link
Copy Markdown
Member

tool.pixi support could be fine to add if you have a good use case, and it's not a lot of additional complexity

@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch from 3b68b23 to 61d64a8 Compare July 28, 2026 14:33
Make the shared `--script <PATH>` selector the entry point for removing
dependencies from PEP 723 scripts. Route Conda and PyPI removals through
the normal remove pipeline, while rejecting named features, environments,
and non-run dependency types that the script model cannot represent.

Keep absent sidecars absent and update an adjacent lockfile when one
already exists. Remove the `pixi script remove` adapter and the now-empty
`pixi script` command namespace.

Address review feedback:

- require explicit initialization instead of modifying arbitrary files;
- keep Conda and PyPI selection explicit through the existing `--pypi` flag;
- snapshot the complete script after both dependency types are removed;
- preserve portable PEP 723 fields and unrelated `tool.*` content while
  removing a Pixi dependency table emptied by the edit; and
- avoid creating a workspace lockfile or local `.pixi` directory.
@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch from 87623df to b721550 Compare July 29, 2026 22:00
manzt added 7 commits July 29, 2026 18:23
Separate ordinary workspace selection from the script-capable selector.
Only init, run, add, remove, and lock expose `--script` at this point;
commands without a meaningful PEP 723 operation no longer advertise or
accept it merely because they support `--manifest-path`.

Replace the recursive manifest-path coupling test with an explicit
allowlist of script-capable commands. Remove runtime rejection branches
from config, task, and workspace commands now that Clap excludes the
selector before execution.
Add `--script <PATH>` to the workspace channel command group. Route add,
list, and remove through the normal channel pipeline while rejecting named
features and environments that the script model cannot represent.

Teach PEP 723 rendering to synchronize explicitly edited workspace channel
and platform arrays without materializing synthetic empty fields. Channel
edits without an adjacent lockfile update only the manifest; an existing
sidecar continues to use the requested lockfile policy.

Address review feedback:

- snapshot the complete script after channel addition and removal;
- preserve portable metadata, Python source, and unrelated `tool.*` content;
- keep an explicit empty channel list after removing the last channel;
- list channels from the implicit default environment; and
- avoid creating an adjacent lockfile, workspace lockfile, or local `.pixi`
  directory.
Add `--script <PATH>` to the workspace platform command group. Route add,
edit, move, list, remove, and auto-detection through the normal platform
pipeline while rejecting named features and environments that the script
model cannot represent.

Pass lockfile policy through the platform API so edits without an adjacent
script lock update only inline metadata, while an existing sidecar continues
to be updated normally.

Address review feedback:

- snapshot the complete script after add, edit, move, and removal;
- preserve portable metadata, Python source, and unrelated `tool.*` content;
- exercise rich platform metadata and declaration-order changes;
- list platforms from the inline manifest; and
- avoid creating an adjacent lockfile, workspace lockfile, or local `.pixi`
  directory.
Add `--script <PATH>` to `pixi tree` and both workspace export
formats. Resolve the inline manifest as the default environment, while
rejecting named environment selection that PEP 723 cannot represent.

Lift the no-sidecar lock policy out of `pixi run` so read-only script
consumers share the same behavior: solve in memory by default, honor an
existing adjacent lock, and report a contextual error when `--locked` or
`--frozen` names a lock that does not exist.

Address review feedback:

- snapshot the environment YAML rendered from mixed Conda and PyPI metadata;
- exercise explicit-spec export both with and without an adjacent lock;
- verify tree output from an in-memory solve and again under `--locked`;
- preserve the complete script and its unrelated `tool.*` content; and
- avoid creating an adjacent lock, workspace lock, or local `.pixi` state.
Carry `tool.pixi.target` edits from the synthetic pyproject document back to
the inline metadata block. This makes platform-specific Conda dependencies
round-trip through `pixi add --script` and lets removal discard the target
hierarchy once its last dependency is gone.

Store PyPI requirements with a dependency-specific index in the Pixi-specific
`pypi-dependencies` table. A native project dependency cannot represent that
index, and previously accepted `--index` input was silently discarded.

Address review feedback:

- snapshot the complete script with a target-specific Conda dependency;
- cover indexed and editable PyPI dependency representations;
- exercise a portable local source dependency;
- snapshot removal of the last target dependency without empty tables; and
- verify no adjacent lock, workspace lock, or local `.pixi` state is created.
Make `pixi run --script` reject `--environment` instead of silently using the
script default, and reject `--skip-deps` because a PEP 723 script has no Pixi
task graph. Validate both before workspace discovery so unsupported requests
cannot create lockfiles or local state.

Close the command-surface audit with parser coverage proving that workspace
name, description, version, registration, requires-pixi, environment,
feature, and task commands do not accept `--script`.

Address review feedback:

- exercise contextual add and remove errors for features and environments;
- cover the hidden host and build dependency modes;
- verify unsupported run options leave the complete script unchanged; and
- assert no adjacent lock, workspace lock, or local `.pixi` state is created.
Rewrite the standalone Python script guide around `--script <PATH>` and extend
it beyond the basic init/add/run flow. Cover portable PEP 723 fields, the
Pixi-specific metadata subset, rich dependency forms, channel and platform
management, dependency trees, exports, sidecar locks, cache isolation, and the
workspace concepts that scripts intentionally do not support.

Update the uv migration guide to map standalone scripts directly to
`pixi run --script`, including the division between portable PyPI metadata and
Pixi-specific Conda configuration.

Regenerate the CLI reference from the current command tree. This adds
`--script` to every supported command, records the reserved `-s` selector,
removes the obsolete `pixi script` namespace and its pages, and leaves
workspace-only commands without a script selector.

Address review feedback:

- document mixed Conda and PyPI scripts with a complete metadata example;
- show indexed, editable, source, and platform-specific dependencies;
- declare lock platforms through `pixi workspace platform` rather than a new
  lock flag;
- explain in-memory resolution versus an adjacent sidecar lock; and
- enumerate tree, export, channel, and platform operations explicitly.
@manzt
manzt force-pushed the manzt/pep-723-conda-exec branch from b721550 to c9e2cc4 Compare July 29, 2026 22:28
@manzt

manzt commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Thanks you for all the feedback. I've updated the PR title/description and tried to address all comments.

This was referenced Jul 30, 2026

@ruben-arts ruben-arts left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you Trevor!

I've tested it, and there are no blocking issues in my opinion.

I've made some small tweaks to documentation and an error when you're not using a .py file.

I would love to see some follow ups which I wrote in the issues: #6725 #6724

This is ready to merge in my opinion!

@ruben-arts
ruben-arts enabled auto-merge (squash) July 30, 2026 13:19
@ruben-arts
ruben-arts merged commit d5fcfef into prefix-dev:main Jul 30, 2026
38 checks passed
@manzt
manzt deleted the manzt/pep-723-conda-exec branch July 30, 2026 14:13
manzt added a commit to marimo-team/marimo that referenced this pull request Sep 11, 2026
This PR remains draft until a Pixi release includes
[prefix-dev/pixi#6648](prefix-dev/pixi#6648).
The required upstream change has merged.

The notebook-owned lifecycle in the previous PR describes operations in
terms of a manifest and its realized environment. This PR supplies those
operations with Pixi and makes the choice explicit at the CLI:

```console
marimo edit --sandbox notebook.py       # uv
marimo edit --sandbox=pixi notebook.py  # pixi
```

```text
NotebookSandbox(source, "pixi")
    -> PixiBackendAdapter
    -> add / remove / sync / packages / launch
```

Pixi reads the same PEP 723 metadata. Package changes use Pixi script
commands, synchronization recovers the interpreter and environment root
from structured output, and package inspection reads `pixi list` JSON.
Launches layer the running marimo through `pixi exec`, so the shared
manifest does not pin marimo to the machine that opened it.

Selecting one manager does not import or probe the other. Pixi rejects
Python interpreter overrides, which its script environments cannot
honor, and falls back cleanly for app-host notebooks without script
metadata. CLI, adapter, and fixture tests cover both managers; the guide
documents the remaining Pixi limitations.
manzt added a commit to marimo-team/marimo that referenced this pull request Sep 11, 2026
This PR remains draft until a Pixi release includes
[prefix-dev/pixi#6648](prefix-dev/pixi#6648).
The required upstream change has merged.

The notebook-owned lifecycle in the previous PR describes operations in
terms of a manifest and its realized environment. This PR supplies those
operations with Pixi and makes the choice explicit at the CLI:

```console
marimo edit --sandbox notebook.py       # uv
marimo edit --sandbox=pixi notebook.py  # pixi
```

```text
NotebookSandbox(source, "pixi")
    -> PixiBackendAdapter
    -> add / remove / sync / packages / launch
```

Pixi reads the same PEP 723 metadata. Package changes use Pixi script
commands, synchronization recovers the interpreter and environment root
from structured output, and package inspection reads `pixi list` JSON.
Launches layer the running marimo through `pixi exec`, so the shared
manifest does not pin marimo to the machine that opened it.

Selecting one manager does not import or probe the other. Pixi rejects
Python interpreter overrides, which its script environments cannot
honor, and falls back cleanly for app-host notebooks without script
metadata. CLI, adapter, and fixture tests cover both managers; the guide
documents the remaining Pixi limitations.
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