# Ban negative extras (!= for extras marker)

**URL:** <https://discuss.python.org/t/ban-negative-extras-for-extras-marker/108022>\
**Category:** Packaging\
**Created:** [July 4, 2026, 7:53am UTC](https://discuss.python.org/t/ban-negative-extras-for-extras-marker/108022 "2026-07-04T07:53:33Z")\
**Posts on this page:** 1\
**Showing post:** 11

<div class="post-metadata">

**Author:** ![geofft](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/geofft/32/20031_2.png) [@geofft](https://discuss.python.org/u/geofft)\
**Post date:** [July 4, 2026, 5:41pm UTC](https://discuss.python.org/t/ban-negative-extras-for-extras-marker/108022/11 "2026-07-04T17:41:29Z")

</div>

> [@notatallshaw](#):
>
> `!=` evaluates cleanly as set non-membership, the problem is in the resolution model: whether a `!=` dependency is present depends on the full set of extras requested for the package, which is not settled until every requirer is resolved, and activating an extra can remove a dependency.

Right, I would like to change this (assuming it’s amenable to uv, pip, etc. resolvers, which I don’t know the details of) to saying that both `extra ==` and `extra !=` are evaluated solely on the extras for this particular instance of the package in the dependency graph. If there is another instance somewhere else in the dependency graph with a different set of extras, then those markers are evaluated again on that set of extras, and the resulting requirements are joined additively, just as if they were two different packages.

But this also made me realize another discrepancy between the spec and the pip/uv implementations. Suppose you have

```python
Requires-Dist: something; extra == "this" and extra == "that"

```

Intuitively, it seems like this should cause `foo[this,that]` to install `something`.

By the rules as you’ve described it, this would _also_ cause `foo[this] foo[that]` (or some equivalent setup, like a dependency chain `foo[this]` → `bar` → `foo[that]`), to install `something`, because you’d be evaluating set membership in the list of all the accumulated extras for every time we encounter `foo` in the dependency graph.

However, as best as I can tell, _both pip and uv evaluate this condition to false_ i.e. never install `something`, which greatly surprises me!

For pip this is because the relevant code (src/pip/\_internal/req/req\_install.py) evaluates markers one at a time:

```py
        if self.markers is not None:
            return any(
                self.markers.evaluate({"extra": extra}) for extra in extras_requested
            )

```

(See [this DPO thread from 2020](https://discuss.python.org/t/the-extra-environment-marker-and-its-operators/4976) about how `packaging` doesn’t provide an API for the actual thing you want here, which is set membership.) I don’t yet understand why uv has this behavior; a cursory look at the code (which I’m not very familiar with) implies it does try to do actual set membership instead of looping through the set and evaluating the markers one at a time.

Konsti or Mike, if it’s easy for you to do, I’d be curious if there are any `/extra ==.*and extra ==/` requirements in the wild, because those are also broken.

---

_[View the full topic](https://discuss.python.org/t/ban-negative-extras-for-extras-marker/108022)._
