Skip to content

extra != '...' environment markers always evaluate to true #14139

Description

@geofft

Given a wheel that has something like this in its metadata:

Requires-Dist: requests; extra != "h"
Provides-Extra: h
Requires-Dist: httpx; extra == "h"

pip will always install requests, whether or not you ask for the [h] extra.

This was mentioned in a 2020 DPO thread from @uranusjr:

The current implementation (InstallRequirement.match_markers(), which calls into packaging.markers) is to evaluate each extra against the marker separately and then combine them. So the dependencies of package[extra1,extra2] would return the union of three marker evaluations:

  1. marker.evaluate({'extra': ''})
  2. marker.evaluate({'extra': 'extra1'})
  3. marker.evaluate({'extra': 'extra2'})

The != operator breaks this logic (all inequity operators do) since dependency; extra != 'extra1' would evaluate to true for evaluate({'extra': ''}) and evalute({'extra': 'extra2'}) and apprear as a dependency, even though extra1 is specified.

I am not aware how pip implemented this prior to PEP 508, and it might as well be that pip’s current implementation is wrong and should be fixed. I find it unlikely, however, since packaging.markers does not offer a reasonable interface to compare multiple extra values to a marker all at once.

This still seems to be the case with current pip (version 26.1.2, tested on Ubuntu 24.04).

There was some recent tightening up of the spec language in pypa/packaging.python.org#1988 which now explicitly mentions bothextra == '...' and extra != '...' as intended to work, although the purpose of the edit was to deprecate nonsensical things like extra > '...' and not so much to fix !=.

My motivation here is that onnxruntime and onnxruntime-gpu are alternatives; they both provide the same files, and whichever one is installed last wins. I want to be able to write a pyproject.toml file with

[project]
...
dependencies = [
  "onnxruntime; extra != 'gpu'",
]

[project.optional-dependencies]
gpu = [
  "onnxruntime-gpu",
]

so that installing the project with no extras gets you onnxruntime, and installing the [gpu] extra gets you only onnxruntime-gpu. However, the environment marker on onnxruntime has no effect and that package always gets installed.

(Curiously, uv has the opposite bug: extra != '...' markers always evaluate to false. I filed astral-sh/uv#20107 about that.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions