Thanks Brett.
To be extremely clear: [[resolved-environments]] is entirely computable from the rest of the lockfile, is that right? It represents environments that were materialized ahead-of-time?
Additionally, to confirm my understasnding: package.dependencies is only optional if unresolved-environments-allowed = false, right? Otherwise, you need it in order to travers the graph?
I find the strategy a little bit resolve-y… As a counterpoint, in uv.lock, we don’t write version specifiers in the dependency list. We just write the dependencies with their markers:
[[package]]
name = "build"
version = "1.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "os_name == 'nt'" },
{ name = "packaging" },
{ name = "pyproject-hooks" },
]
If the package is ambiguous (i.e., there are multiple versions in the lockfile), then we include the version and source – here’s a fictional example for anyio > 3 ; sys_platform == 'darwin' and anyio < 3 ; sys_platform == 'win32':
[[package]]
name = "foo"
version = "0.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio", version = "2.2.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'win32'" },
{ name = "anyio", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform == 'darwin'" },
]
(This is a trick we borrowed from Cargo, which only shows versions in the dependency list if there are multiple versions of a given package in the lockfile, and leads to a very succinct representation. Others may disagree with it.)
The use of (name, version, source) means we don’t need to worry about version specifiers, parsing them, evaluating them, etc. at install time. It also means that lookups are very fast, because we index the lockfile by (name, version), and so can lookup dependencies without having to scan the table.
I think requiring == in any specifiers could achieve a similar effect but personally I find it nice to have a schema that can’t even represent those ambiguities. (There may be a reason that I’ve overlooked as to why we’re writing version specifiers though.)
Will continue thinking on whether this could work for us.