In PEP 832: virtual environment discovery - #71 by ofek, @ofek said that he “only and ardently support[s my] alternative pull-based idea from the private pre-PEP discussion where the desired environment manager is defined by metadata in pyproject.toml files.” That is not the current approach that PEP 832 takes, not because it’s wrong, but because I don’t know if such a fancy solution is necessary. But I think it’s worth talking about the idea to see if I’m wrong and should change PEP 832 accordingly.
So what is this idea that I had which Ofek is referring to? Basically it’s to specify a CLI call to make in a configuration file that returns JSON to say where the environments for a project are. Unfortunately there are a lot of “firsts” in that short idea. Let’s break this all down.
Why
So why even do this fancy idea? One is to support multiple environments easily. PEP 832 is purposefully simple, and so it has avoided supporting multiple environments. But if we go this fancier solution, then we might as well support them upfront.
Two, supporting different types of environments (i.e. virtual and conda). Reasons here are the same as multiple environments.
Three, it moves to a pull model. With PEP 832 and .venv redirect files and such, you need to push the environment by creating it first. By having you e.g. code editor pull the info by calling a tool, it gets rid of any bootstrap issues of having not created a .venv file yet. And by having the tool you call that can create any environments that are needed to start work, it helps with the bootstrapping problem of creating the environment first before launching your editor (but creation is not required).
Where
You need to write down somewhere this CLI call you want to make. In a [workflow] table in pyproject.toml is the simplest, but that only works if the project mandates a specific tool to use (e.g. you use a plug-in with Hatch for your project). What if my project has no opinion about what tool to use to manage your environments? Do I still have to make a decision for you to provide this even if it would be arbitrary in that case?
I would argue there should be more flexibility to specify the CLI call just for yourself, but that gets us to a “first” as there is no such thing as a “local” pyproject.toml. Do we have tools potentially read from a pyproject.local.toml that’s next to pyproject.toml, or maybe a pyworkflow.toml? How about a per-user or machine-wide config that gets overridden the more specific you get?
And do you support overriding anything in a project’s pyproject.toml? Could you override [project] or even per-key like project.version? Or only [workflow] and [tool]?
My gut says:
pyproject.local.toml- Support specifying next to
pyproject.toml, per-user, and per-machine - You can override keys in
[workflow]and[tool]viamachine | user | pyproject | local(pyproject.local.tomlin the project >pyproject.toml> per-user > per-machine)
And I don’t view having some support for setting things locally as optional.
How
The next question is how do you specify the CLI to call? I think we gain a [workflow] table and it has an environments key. That key takes a table with the following keys:
cmdorshellcmdis an array of strings of a subprocess command to runshellis a string to run in a subshell- Only one of these two keys can be specified, but one is required
requiresis optional and specifies the requirement for the tool so you could install it from e.g. PyPI as necessary
This is another “first” as we don’t have any CLI API anywhere.
When the tool is called, the PY_PROJECT_PATH environment variable holds the path to the pyproject.toml or the pyproject.local.toml file for the project. It’s required that one of those files to be found, else there’s no anchor point to be working off of to know what project you’re working on for selecting the proper environment(s). A search from the current working directory up for those files works for me as it’s simple, easy to understand, and doesn’t get the per-user or per-machine config in the way most of the time.
What
Now that we know how to call a tool, what should it do? Well, it should return JSON on stdout about the environments for the project:
{
"interpreters": [
"path": "...",
"type": "...", // "conda", "virtual", "global", "x-..."
"name": "..." // Optional
"run": { // Optional
// Requires one of "cmd" or "shell"
"cmd": ["..."],
"shell": "..."
}
],
"default": 0 // Optional
}
- “interpreters” is an array of objects
- “path” represents the path to the environment
- It should be unique to the environment
- If possible, point to the Python interpreter
- “type” is the type of environment
- “conda” is for a conda environment
- “virtual” is a virtual environment
- “global” is a globally installed Python interpreter
- Anything with an “x-” prefix is some custom type of environment
- “name” is a string acting as a label to help identify the environment to the user
- “run” is optional and specifies how to run the environment
- If omitted then “path” is assumed to point at the Python interpreter
- “cmd” is an array of strings for a subprocess
- “shell” is a string to run in a subshell
- Only one of “cmd” or “shell” can be specified
- Arguments to the Python interpreter are expected to be appended at the end (e.g.
-m ...would be added appropriately at the end of “path”, “cmd”, or “shell”) - Useful for requiring e.g.
conda runto run the interpreter
- “path” represents the path to the environment
- “default” is optional and an integer indexing into “interpreters” to specify the default environment
- It’s possible to pipe the result into
jqto at least select the default interpreter:jq '.default as $i | .interpreters[$i]'
- It’s possible to pipe the result into
The tool being called may create environments before returning the JSON. In fact, I would encourage creating a single environment instead of returning an empty result of “{}”.
Who
So far, Hatch seems to really want this.
So that’s the proposal. Good and worth pursuing? Bad and stick with what’s in PEP 832 today? It’s going to take a good amount of support to pivot PEP 832 since this is:
- More work for me and everyone else who wants to support this
- I already have a decent amount of tool support for PEP 832 already