From 105ab9614ec8d131de08f7bfe879f30cea282e54 Mon Sep 17 00:00:00 2001 From: konstin Date: Sat, 4 Jul 2026 09:50:14 +0200 Subject: [PATCH] Ban negative extras Negative extras (`extra !=`) are currently technically allowed in the grammar, but they behave strangely: `extra !=` means that package could transitively remove a package from selection through activating an extra. This turns incremental resolution into a global problem: We can't exit a resolution with a conflict when we've exhausted all versions in range including backtracking, we also have to exhaustively search for any package version that may activate the extra which deactivates the `extra !=` requirement which is part of the conflict. This would be fatal to resolver complexity and performance, preventing most of the short-circuiting that makes resolving tractable. Much weirder, with negative extras, adding a new dependency can make a previously impossible resolution solvable: ``` [project] name = "foo" version = "0.1.0" dependencies = [ "numpy >2", "numpy <2; extra != 'old-science'", ] ``` Only by adding a package with `Requires-Dist: foo[old-science]`, this resolves. Using PEP 621, users only get positive extras: `project.optional-dependencies.foo` gets translated into a conjunction of `extra == "foo"` with each requirement's marker. While this may look like a breaking change, it only clarifies the current state of support in tools. From [pip not supporting it](https://github.com/pypa/pip/issues/14139) and [uv not supporting it](https://github.com/astral-sh/uv/issues/20107), we know it is not currently used and safe to remove. This solves the [confusion for users who want to use negative extras for conflicting packages](https://github.com/astral-sh/uv/issues/20107). --- source/specifications/dependency-specifiers.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/specifications/dependency-specifiers.rst b/source/specifications/dependency-specifiers.rst index d66f77503..d9054bec8 100644 --- a/source/specifications/dependency-specifiers.rst +++ b/source/specifications/dependency-specifiers.rst @@ -457,9 +457,8 @@ DO NOT need to define these fields. The ``extra`` field is also special, as it expects set-like behaviour, but predates the addition of ``Set of strings`` as a defined marker field type. -Accordingly, ``extra == "name"`` in a dependency declaration is similar to -``"name" in extras``, while ``extra != "name"`` is similar to -``"name" not in extras``. For dependency marker evaluations, the set of extra +It only supports one operation, ``extra == "name"``, which is similar to +``"name" in extras``. For dependency marker evaluations, the set of extra names used for these comparisons is the full set of requested extras for *that particular package*, whether requested directly in a top level dependency declaration, or indirectly in a transitive dependency declaration. Other