PEP 735: Dependency Groups in pyproject.toml

Might be a little late, but currently the PEP doesn’t specify what happens when a group includes itself:

[dependency-groups]
foo = ["a-n-plus-b", { include-group = "foo" }]

It does say, however (emphasis mine):

A Dependency Group Include includes the dependencies of another Dependency Group in the current Dependency Group.

And then there’s the case of groups referring each other circularly:

[dependency-groups]
foo = ["a-n-plus-b", { include-group = "bar" }]
bar = [{ include-group = "foo" }]

I suppose tools should report these cases as errors, but it would be nice to have that spelled out in either the PEP or the packaging specifications.

The PEP says

Dependency Group Includes MUST NOT include cycles, and tools SHOULD report an error if they detect a cycle.

3 Likes

I think this may just be a minor copyediting issue: the last paragraph in that section contains two unrelated sentences, and this is the second one.

Even knowing it was there based on your quote, it still took me a moment to find it.
Could be worth just making that two paragraphs instead of one.

Not sure why I missed that. Thanks!

I might be biased because I wrote that text, but it reads fine to me.

The first part says that an include names “another group”, so it implies but does not clearly state that self includes are forbidden. The last paragraph says that

  • includes are expanded recursively
  • cycles are forbidden

So if we want to clarify, I might do it where the “an include names another group” language is, at the start of the section, to add

A Dependency Group MUST NOT include itself.

I think that fits best?

1 Like

I agree that it reads fine and “no cycles” follows from the previous sentence. Editing due to a single misunderstanding feels like an overreaction.

4 Likes

There’s also room to spell this out in more detail in the packaging specification. As long as we agree that the PEP unambiguously addresses this, and nobody is under the impression that a self-include would be allowed, it’s not necessary to clarify here.

3 Likes

Congrats on the acceptance of the PEP!

May I ask a clarification of what “non-package projects” means? (I don’t feel guilty about asking this 7 days late :grimacing: because probably it’s terminology that ought to be clarified outside of this PEP…)

The Motivation section implies they are “projects which do not build distributions”. That reminds me of the output of uv init --app (default behavior for uv init), which produces a pyproject.toml that doesn’t have a [build-system] table (hence “Do not set up the project to be built as a Python package.”, as per their documentation) but it does contain a [project] table.

Maybe it’s buried somewhere in the thread, but it seems to me that we’re talking about 3 different kinds of projects:

  1. No [build-system] and no [project] (“collections of scripts”, main motivation to not use extras)
  2. No [build-system] but existing [project] table (what uv calls “applications”, --no-package flag of uv init, presence of [project] already triggers some assumptions)
  3. Both [build-system] and [project] tables (what uv calls “libraries”, --package flag)

It seems that the PEP kind of mixes (1) and (2) when talking about non-package projects?

Maybe folks can point out how they’re using (1) and (2) in the wild (beyond the theoretical examples presented in Appendix C).

As soon as a [project] table is present, a number of tools will treat the source tree as containing a package, which drives all kinds of behaviors. It is not keyed off of the [build-system] table, and that behavior is specific to uv.

So in the broader ecosystem, any behaviors which you might associate with uv’s handling of no-build-system are probably tied to no-build-system and no-project.


Unless I’ve lost track, the greatest depth of discussion happened in the thread which most-directly preceded this PEP:

(and where, funnily, I was pretty much just an observer and didn’t comment much if at all)


I would say that we are thinking about at least 3 different kinds of projects, probably quite a few more. Collections of scripts are amongst the support targets, but so are…

  • packages which are applications
  • applications which are not packages (e.g., some non-python-related build process which incorporates python components)
  • libraries (i.e., the most “traditional” packages)
  • applications which can use a python build process but don’t really care (e.g., webapps which need dependency locking but don’t have any particular need to build a wheel)

I would not try to define the suite of covered scenarios in any prescriptive or narrow way. Any directory containing a pyproject.toml file is covered, if the developer wants to use [dependency-groups] in a spec-compliant manner.

2 Likes

I always forget that the default build-backend is setuptools.build_meta:__legacy__… thanks for the quick turnaround :pray:

1 Like

I realize this is beyond the scope of the finalized PEP, but this feels related to me - happy to start a new topic though:

I think of a common use case for PEP 735 as being a way to define dependencies for each of multiple “environments” that might serve different purposes/tasks. However, I often think of a “Python environment” as meaning not just the installed packages but the Python interpreter too. Often users need to attach to different environments a specification of the python version.
Many tools do this in different ways currently, e.g. tox, Hatch’s concept of environments, uv checks a .python-version file. Is this something that would be worth standardizing?

If you want to change the topic, it would be a good idea to start a new thread. This one’s already 352 posts deep…

2 Likes

We released support in uv today. Implementation details in the tracking pull request for those who are interested. We don’t support conflicting groups yet, nor do we support using groups without a [project] definition, but we plan to support those use-cases eventually!

25 Likes

Pex now supports this as well: Release pex 2.23.0 · pex-tool/pex · GitHub

6 Likes