PEP 621: how to specify dependencies?

To ensure that PEP 621 doesn’t inadvertently allow the definition of dependency declarations that can’t actually be published as part of the resulting artifact metadata, I think it makes the most sense for it to specifically use PEP 508 markers.

However, I’d suggest a hybrid of the examples Brett gave, where a table is still used to separate the dependencies on different packages, but the values within the table are just PEP 508 strings rather than subtables:

# simple
colorama = "*"
# environment dependent
django = [
    ">=2.0; os_name!='nt'",
    ">=2.1; os_name=='nt'" # Affected by Windows-specific bug in 2.0
]
# with extras defined
win32 = "[all] >2.2.0, <3.0.0; os_name == 'nt'"

The normal case would just be a single string, but a list of strings would also be allowed to cover the “multiple mutually exclusive and/or mutually compatible environment markers” case.

It isn’t as self-documenting as the table version if you don’t already know the PEP 508 syntax, but it’s much easier to translate into explicit pip install commands

As noted in Brett’s initial post, Pipfile/pipenv mostly went with the “table-with-subtables” option, but there’s a shorthand for the simplest case similar to the one I describe above: if you’re only specifying a version constraint, you can just use a string.

That means the simplest way to specify a dependency is as:

pkg_name = "<PEP 440 specifier>"

The wildcard string pkg_name = "*" is a non-PEP-440 shorthand for “any version”, since pkg_name and pkg_name= aren’t legal TOML, the full spelling (pkg_name = "== *") is quite verbose, but pkg_name = "" and pkg_name = {} didn’t feel like they conveyed “any version” strongly enough.

poetry offers a similar shorthand for version specifiers (although the syntax for some of the comparison operators differs from PEP 440).

While anything more complex than that uses a subtable (typically written in the inline-dict format, rather than as a full multi-line table), many of the options that Pipfile and poetry support (e.g. editable installs, local path installs, direct URL installs, direct-from-VCS installs) are ones that should only arguably be supported in pyproject.toml as part of intended-for-publication dependency declarations, and it’s those local development and private deployment focused features that either aren’t covered by PEP-508 or are covered by PEP 508 but aren’t allowed in PyPI uploads (and hence aren’t very well known or supported by libraries) that really motivated using the table format.

It’s OK for poetry & pipenv to support unpublishable dependency declarations, as they’re both used as environment managers instead of or as well as package build tools (exclusively in pipenv’s case, optionally in poetry’s case). By contrast, it’s not really OK for PEP 621 to support unpublishable dependency declarations.

I’m aware that using full PEP 508 markers in PEP 621 would likely create demand for Pipfile/pipenv and poetry to also support the extended input format, but I don’t see that posing a major problem in the long run (the tools already cover the relevant semantics, so it should mostly just require adjustments at the file parsing layer).

1 Like