PEP 751: now with graphs!

As a user, I like the idea of a compromise @brettcannon I think I understand the pyproject.toml comparison. I think it may take a while for the mental model to shift from current discussions.

Fwiw, pip does have some of the machinery towards a v2 requirements lockfile. The installation report.

And maybe the tool maintainers we need to consider are:

  • (pip + pipcompile)
  • poetry
  • PDM
  • hatch
  • uv

One way to help consolidate the ecosystem, might be for this initiative to roll pip and pipcompile together. I know it will be controversial and my perception so far is that the pip maintainers are unlikely to welcome the idea. I’m just speaking as a user that it feels like pip holds this unique place that it’s important, it has a huge responsibility to not break backwards compatibility, but that it’s also not a very complete tool for a lot of users. That’s completely understandable and reasonable of course. Not a criticism. pip + pipcompile is more comparable to the rest in the list. Just my 2c.

I prototyped generating a lockfile from the pip installation report, very early in this discussion. At the time, it wasn’t hard. I don’t know if the more recent ā€œwith graphs!ā€ versions of the standard would be as easy, but I’d hope it’s not impossible.

From pip’s perspective, I see three places that might gain lockfile support:

  1. Installing from a lockfile. This is relatively uncontroversial, with the only potential issue being whether installing while ignoring the [tool] section of the lockfile is allowed.
  2. pip install --report --report-format=lockfile. This seems like it would be easy enough. It would be equally easy for someone to write a tool that read the existing pip report and generated a lockfile. So this one will probably come down to whether anyone feels it’s worth the effort to write a PR for pip.
  3. pip freeze --format=lockfile. This is potentially possible if PEP 710 gets accepted. But it will likely be unreliable until enough time has passed for PEP 710 metadata to be universal. It would also be easy enough to write as a 3rd party tool, so I wouldn’t expect it to be added to pip particularly soon.

The other question is pip-tools, which you grouped with pip. I don’t know if it’s reasonable to do that - yes, many users use pip and pip-tools in combination, but the two projects are independent, and we don’t collaborate in any way to provide a ā€œsharedā€ workflow. I don’t see any reason why this would change for lockfile support.

I certainly would be against this. If nothing else, I don’t think we need yet another workflow tool, which is what ā€œpip plus pip-toolsā€ would inevitably become. But merging pip and pip-tools is offtopic for this discussion[1], so I suggest we don’t worry about it for now.


  1. and frankly, it’s not really for the community to decide - it’s up to the project maintainers ā†©ļøŽ

1 Like

That’s a big ask for an idea I came up with while trying to fall asleep. :sweat_smile:

Think about the spec covering what it currently does, but providing flexibility to have tool-specific ways to say how to get code or what to install, and in such a way other tools can know what parts are tool-specific.

The latter. I think @charliermarsh once pointed out that anything in [tool] would have to be basically harmless ā€œFYIā€ info or else you break installers w/ the current locker/installer dichotomy. I’m saying open it up so it’s okay for tools to have stuff that ultimately mandates that same tool be used for installing.

I think others pointed it out, but a lock file that didn’t have any tool-specific info could still be read by pip (and potentially produce a lock file).

That’s one possibility. Another is we keep a graph format but provide ways to specify tool-specific ways to make relationships between packages and/or how to install the code.

That’s up to the tools, but I assume that they would only reach for tool-specific stuff when necessary. As well, I would hope they would contribute back with future PEPs to expand what they have found to be useful.

Correct.

Yes, I assume a basic level of interoperability. Think of the PEP either in its current state or back in its ā€œset of packagesā€ state, but where tools can have tool-specific details that they need for some extended support they provide which others simply won’t understand. It might make that lock file not fully usable by other tools, but I also don’t assume it will make it an opaque file format either (e.g., package name and version is pretty well understood at this point :wink:).

I don’t think it would be.

Probably not; you would error out. But hopefully either that’s not going to be the common case or we come up with some way to say, ā€œyou won’t understand this properly for installing, so don’t even tryā€ and either you export out to a lock file with no [tool] or you have to give up because it was never going to work due to some exotic thing the tool can do.

1 Like

I think a ā€œset of packagesā€ approach is sufficient for Poetry. [1] If I remember correctly, the only thing that was missing was a means to specifiy the resulting dependency groups a package is required for and the resulting markers per group. We can still put this in the tool section of each package but it will mean that another installer, which does not look into tool.poetry, will not install the correct set of packages under certain circumstances.

Considering my previous point, I suppose the tool that produces the lock file can tell if the tool section is harmless or not. Maybe, it is worth to add a field for this information (outside the tool section) if we continue to pursue this approach?


  1. It might not be sufficient anymore, if we decided to support multiple entry points in a monorepo use case as uv does, but there are no plans to do that at the moment. ā†©ļøŽ

1 Like

So a way to have named groups where the group itself had marker requirements overall? And still have marker requirements per package entry? I’m personally fine with that. Would you expect to consolidate markers to the group and then only have markers for packages if they are e.g., OS-specific?

I was thinking the same thing, like having an installer-agnostic key that signifies if the there’s critical information in any [tool] section which would impact installation.

2 Likes

Not exactly. More something like package A, which might be a transitive dependency, is required for groups dev and test. For group dev it is always required, but for group test it is only required for sys_platform == "win32". (That is not something specified in the pyproject.toml but the result of dependency resolution.) You need something like

[[packages]]
name = "A"
groups = ["dev", "test"]
marker = {"dev": "*", "test": 'sys_platform == "win32"'}

If marker is just a single marker string, we can only put something in that is either wrong for dev or wrong for test. Alternatively, we put it in the tool section of the package and specify installer-agnostic = false.

I assume you’d need a similar thing for extras?

No, because there is an extra marker. If there was a marker for groups, we would only need one marker string per package. However, I suppose it does not make sense to have a marker for groups because groups are not published in the package’s metadata.

Right, and you’re only dealing with a single project, so the extra is unambiguous – got it.


While we’re not yet aligned on the goals, I think there’s sort of a gradient of options being considered here:

  1. A new requirements.txt: captures a single scenario, across many platforms.
  2. The PDM-style lockfile from the previous iteration of the proposal. It’d still be focused on a single project, but it would presumedly support installing extras and dependency groups for that project.
  3. The lockfile in this current iteration, which allows packages to be tagged as part of arbitrary groups, with the intent of supporting multiple roots (e.g., workspaces) in addition to extras and dependency groups.

I find a fourth option (4) sort of intriguing, inspired by Brett’s most recent comments, which is: the standard just covers [[packages]], a flat list of packages, but any metadata connecting the packages or indicating when they should be installed, etc., needs to go in [tool]. That way the standard just covers ā€œlisting a set of packagesā€, which makes it very flexible while still enabling tools like Dependabot to operate on the standardized format. Then, uv, Poetry, PDM, etc. could decide what they want to write in [tool] to support their CLIs atop those lockfiles. And we can continue building out functionality in uv (like locking build dependencies) without being constrained by the standard.

My current opinions are as follows:

  • As I said before, if we want to do (1), that seems fine with me. It seems useful to standardize this, even as an export format.
  • If we do (2), I would… try to find a way to make it work for uv. Maybe we’d write a separate lockfile for each workspace member? Then we’d be in ā€œthe sameā€ position as Poetry and PDM. I think it would be a worse experience for uv users and would create a lot of complexity for us, but I’d still try. (Or maybe we’d find a way to solve this with tool.uv? I’m not sure.)
  • If we do (3), I’d push for several changes to the schema, but I’m trying to ignore those for now. I remain worried that (3) is a little too closely coupled to the tools themselves, as per my previous comments… But if we no longer view installer interoperability as a goal, then I think it’s possible for us to align on something here, and I’m game to try.
  • If we do (4), that’s also fine with me. In fact it might be my preference, since it seems to nicely balance ā€œthe things we want from standardizationā€ with not constraining tools.

Finally, it was suggested above that one outcome here could be that Poetry and PDM use the standard as their first-class formats, but uv just exports to it. Honestly, I’d be really disappointed by an outcome like that, and I think it’d be a big loss for the ecosystem to standardize on something that willing implementers already can’t use today. So, as my comments above reflect, I’m trying very hard to avoid that while still sharing my honest opinions :slight_smile:

4 Likes

Reading the options you’ve laid out, I think it’s worth noting that you’ve mentioned how (3) is ā€œtoo close to tool implementationsā€, but (2) has that same problem as well. It’s too close to the way that PDM and Poetry lockfiles work, which makes it hard to adapt for uv.


Here’s a particular experience which I (and I bet many other users) would like:

  • I have a variety of dependency lists (groups or requirements files)
  • I have a range of supported interpreter versions
  • I ask the locker to lock ā€œthat matrixā€ of things
  • I later ask an installer to ā€œinstall a dependency group or requirements file, but use the lockā€, and the installer can find the locked data, lookup the current interpreter version, and give me a reproducible install or a hard error if it can’t be done

I think that sort of workflow can be satisfied under several possible lockfile specs, but it strikes me as easier if there’s a single lockfile with all of the data, rather than a file per possible environment.

1 Like

Or you list the project twice with different markers (and I’m not saying I think this is better, just that it’s an option). After the whole ā€œversion numbers with source trees are a painā€ discussion I have been thinking about how to state the requirement that each listed package needs to be unique overall, but without being so simplistic as saying just ā€œname and versionā€. In this instance it could include differing markers and groups.

I believe @ncoghlan once suggested introducing a marker for groups.

Yeah, I think I’ve hinted at that possibility. I would probably want to tweak it a little, though, and have it piggyback on option (1) by saying the simple requirements case is covered when installer-agnostic = true – bikeshed later on details – is set and thus the lock file is something anyone can install from (e.g., pip could freeze an environment and also do an install from the same file as could anyone else). I would think [[packages]], markers per package, and that’s it; no extras, no groups, etc. Think requirements.txt but that it can be cross-platform thanks to the markers. Otherwise we can toss the markers idea out and have a top-level environment key that specifies under what circumstances the list of packages blindly apply to. Either way, it would be the greatest common denominator that anyone could directly work with and still act as an export target. It also doesn’t expand the feature set either way of what requirements.txt files currently supports, so we know the baseline feature set does meet a need.

But when installer-agnostic = false then all bets are off and tools can do whatever they want with no expectations anyone will do more than read the list of packages that may get installed somehow.

But I don’t know what @radoering or @frostming think of this idea (nor have I run this by GitHub yet to see what they think; I’ll go see if I can chase someone down after I post this). I also don’t know if you like this tweak, @charliermarsh . I’m personally happy to go with this idea if the tool maintainers think it’s a good compromise.

Yep, but making everyone happy sure is hard. :sweat_smile:

I may have suggested it as it’s a bit easier to reach that goal since everyone gets something out of this in that instance compared to right now where we are at an impasse. But hopefully the option (4) above works out.

But @ofek has said the exact opposite, so there’s no clear answer here.

3 Likes

Just to clarify, I think he was saying so in the context where the locker and installer are (or are wrapped by) the same tool.

ā€œSame toolā€ vs ā€œdifferent tools interoperatingā€ is one of the axes of this problem which I should probably be more careful to specify, since it dramatically changes things.

You’re working hard on it though! We should try to be easier to please. :stuck_out_tongue:

1 Like

Yes, I think that’s correct without looking back at the exact context.


Speaking of tool interoperability, I would like to call attention to this discussion I opened for pip: Determining cross-platform resolution strategy Ā· Issue #13111 Ā· pypa/pip Ā· GitHub

I don’t speak on behalf of the maintainers, this is just my opinion, but I feel almost certain that the current proposal is a requirement for pip to ever support proper cross-platform resolutions. It’s unclear to me (I think not currently) that there is a desire for pip to gain more functionality similar to a full-fledged project manager like Hatch or UV, and I agree with that preference. As such, I think the previous requirements.txt-style spec is more compatible with the current and future architecture of pip.

I was a bit saddened by that realization but I think it’s fine after more contemplation. pip ships with Python and supports easy bootstrapping of dependencies targeting the interpreter with which it is installed. It is very good at this and serves that forever-critical need.

edit: To be explicit, I’ve updated my preference to the current proposal rather than the previous requirements.txt-style spec. This is the opposite of Charlie’s update :sweat_smile:

3 Likes

I mostly haven’t been following the thread for the past few weeks (the size of the ā€œunreadā€ number scared me), but I wanted to chime in to say I really like this idea due to the way it allows the ecosystem to evolve:

  • in the installer agnostic variant, we get our ā€œstandardised cross platform requirements.txtā€ replacement
  • in the installer specific variant, we can still get standardised discovery of what the expected installer is (and how to install and invoke it)
  • over time, the capabilities of the installer agnostic variant can be expanded (similar to the migration of dependency group support from tool table entries into standardised fields)

I’d paint the bikeshed as a full [installer] table rather than as just a boolean flag, though. That way in addition to the simple declaration of the installer’s name, it could also include a full transitive lock of the installer and its dependencies, as well as the command to execute for the default install.

4 Likes

I think a combination of (1) and (4) will be fine for Poetry.

As already said, extras can be covered via markers. (Not sure if every installer will understand it, but anyway.) I wonder if groups should not be considered at least? (Just the simple case where you do not need different markers per group.) Otherwise, each lock file (with all groups) from a pyproject.toml with groups will not be installer agnostic, correct?

1 Like

Exactly. With the file format being versioned it should definitely allow for expansions in the future as experience tells us what should be expanded as something every installer should support.

Yeah, I realized after I posted that people were probably going to at least want the tool name, although you can at least partially infer it from whatever tool has a [tool] table.

Great! So that leaves us at:

  • Poetry: :+1:
  • uv: maybe (waiting to hear from @charliermarsh about the option (1) modification)
  • PDM: unknown

And I’m trying to find someone at GitHub from Dependabot and/or the dependency graph to tell me this idea works as an external validation.

I’m personally fine with that, but @charliermarsh seemed a little skittish on pushing too far out past what a requirements.txt file does today. But honestly, if we drop the custom markers then it’s no different than saying we just came up with a way to squish multiple requirements.txt files together and I don’t think that’s controversial.

So I think I just talked myself into liking the idea. I will probably say there is a required ā€œDefaultā€ group – bikeshed on the name later – so that it’s always clear what should be installed by default. Whether we want to make it implicit when there’s just the one group is for a later discussion.

Correct.

6 Likes

The spirit of PDM is to adopt Python packaging standards as much as possible, so it is also very likely that PDM will use the new lock file to replace pdm.lock.

I agree not to introduce groups into the new lock file standard, otherwise it will become too complicated. (1) + (4) too here but I tend not to put too many stuff inside.

4 Likes

My current stance is that we should pursue (1). requirements.txt is popular, and replacing it with a standardized, secure format would be impactful for users of all tools.

On the other hand, I don’t really see value in pursuing (2), (3), or (4) right now. I’m just not convinced that they’re solving problems in a way that’s worth the costs. Specifically, what are the benefits? The fact that we’re not pursuing installer or resolver interoperability means…

  • Dependabot will only partly work. Dependabot won’t be able to put up PRs to upgrade your dependencies, since that will require tool-specific commands and knowledge. That’s most of the value of Dependabot, at least in my experience. In the current framing, Dependabot would be limited to security advisories (unless I’m misunderstanding). So I don’t think these formats really solve the ā€œfairnessā€ problem discussed above. Dependabot would still need per-tool support to be useful.
  • Users won’t have to learn about poetry.lock, pdm.lock, uv.lock, etc… But pylock.toml won’t actually be interoperable. So users will have to learn that despite using the same filename, they’re not the same format, nor are they interchangeable between tools. I could actually see this being even more confusing for users than the status quo.

I understand the desire to standardize, but is diversity in this specific problem space actually harmful? Is it actually causing problems or confusion for users? I know this might be an unpopular opinion, but if we standardize on (2), (3), or (4), we’ll be inherently placing constraints on these tools. Suddenly, we’ll have to think about the interaction with the standard for every new feature that we’re pursuing (conflicting dependencies across extras and groups, locking of build constraints, etc.).

So, if we do (1), I’m confident that uv will fully support it. If we do (2), (3), or (4), I’m not sure. I suspect we’re more likely to stick with uv.lock.

Two addendums, though…

  1. I do think (3) could be worth doing if we can solve the installer and resolver interoperability questions. But I don’t think it’s feasible to do today. At least for us, the format, capabilities, and understanding of the problem are still evolving dramatically. I would prefer to revisit it in the future, even if that is not the ā€œnear futureā€.
  2. I know that the line between (1) and (4) is fuzzy, but the way I might frame it is: I would prefer that we not put tool-specific metadata in the file.
4 Likes

I’m in favour of (1) + (4). I don’t think (1) removes the [tool] section from the specification, and I wouldn’t want it to. So all (4) is really adding is a way for tools to provide some metadata about whether the data in the [tool] section affects installability or not.

If that’s your preference, then even under (1) + (4), I believe that uv can simply not use the [tool] section, and refuse to import lockfiles that do use it.

I’m not at all clear why you’d be unable to do with (1) + (4) exactly what you would do with just (1). In particular, if you can support the standard if it’s (1), then surely you can support it just as well in the case of (1) + (4). Just treat it as if it were (1).

Maybe I’m misunderstanding (4), though. As far as I can tell, all (4) seems to add is the possibility to set installer-agnostic=false, and uv can just reject all such lockfiles unless they were produced by uv…

2 Likes

Can someone re-quote what we’re all referring to with 1, 2, 3, 4? I can’t find it anymore and it’s hard to keep track of.

5 Likes