Repository: pypa/pip
Pull request #13361 (2025-04-28 17:35:19)

Bump setuptools from 79.0.0 to 80.0.0 in /build-project
Bumps [setuptools](https://github.com/pypa/setuptools) from 79.0.0 to 80.0.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/pypa/setuptools/blob/main/NEWS.rst">setuptools's changelog</a>.</em></p>
<blockquote>
<h1>v80.0.0</h1>
<h2>Bugfixes</h2>
<ul>
<li>Update test to honor new behavior in importlib_metadata 8.7. (<a href="https://redirect.github.com/pypa/setuptools/issues/4961">#4961</a>)</li>
</ul>
<h2>Deprecations and Removals</h2>
<ul>
<li>Removed support for the easy_install command including the sandbox module. (<a href="https://redirect.github.com/pypa/setuptools/issues/2908">#2908</a>)</li>
<li>Develop command no longer uses easy_install, but instead defers execution to pip (which then will re-invoke Setuptools via PEP 517 to build the editable wheel). Most of the options to develop are dropped. This is the final warning before the command is dropped completely in a few months. Use-cases relying on 'setup.py develop' should pin to older Setuptools version or migrate to modern build tooling. (<a href="https://redirect.github.com/pypa/setuptools/issues/4955">#4955</a>)</li>
</ul>
<h1>v79.0.1</h1>
<h2>Bugfixes</h2>
<ul>
<li>Merge with pypa/distutils@24bd3179b including fix for <a href="https://redirect.github.com/pypa/distutils/issues/355">pypa/distutils#355</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/pypa/setuptools/commit/aeea79266d82f99dbe556126b90b64215a663a2c"><code>aeea792</code></a> Bump version: 79.0.1 → 80.0.0</li>
<li><a href="https://github.com/pypa/setuptools/commit/2c874e78f4240963f74debcaadcccb97cb302ded"><code>2c874e7</code></a> Merge pull request <a href="https://redirect.github.com/pypa/setuptools/issues/4962">#4962</a> from pypa/bugfix/4961-validated-eps</li>
<li><a href="https://github.com/pypa/setuptools/commit/82c588aedd8142e7615031358e2d2640213a351d"><code>82c588a</code></a> Update test to honor new behavior in importlib_metadata 8.7</li>
<li><a href="https://github.com/pypa/setuptools/commit/ef4cd2960d75f2d49f40f5495347523be62d20e5"><code>ef4cd29</code></a> Merge pull request <a href="https://redirect.github.com/pypa/setuptools/issues/2908">#2908</a> from pypa/debt/remove-easy-install</li>
<li><a href="https://github.com/pypa/setuptools/commit/85bbad4945d874a2444e4531c74c5074cdeca010"><code>85bbad4</code></a> Merge branch 'main' into debt/remove-easy-install</li>
<li><a href="https://github.com/pypa/setuptools/commit/9653305c35a143b8d1bad2c190f918887dd1e6d5"><code>9653305</code></a> Merge pull request <a href="https://redirect.github.com/pypa/setuptools/issues/4955">#4955</a> from pypa/debt/develop-uses-pip</li>
<li><a href="https://github.com/pypa/setuptools/commit/da119e7e996b00b6e26f79995bec55684a3fabbe"><code>da119e7</code></a> Set a due date 6 months in advance.</li>
<li><a href="https://github.com/pypa/setuptools/commit/a7603da5d3c709f6f01c8df8031ba7a7ae7959a0"><code>a7603da</code></a> Rename news fragment to reference the pull request for better precise locality.</li>
<li><a href="https://github.com/pypa/setuptools/commit/018a20cb130e9357f39c176b59c83738a09d7daa"><code>018a20c</code></a> Restore a few of the options to develop.</li>
<li><a href="https://github.com/pypa/setuptools/commit/a5f02fe88d46e963bc470a60a9f8613d7f889d49"><code>a5f02fe</code></a> Remove another test relying on setup.py develop.</li>
<li>Additional commits viewable in <a href="https://github.com/pypa/setuptools/compare/v79.0.0...v80.0.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=setuptools&package-manager=pip&previous-version=79.0.0&new-version=80.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/NEWS.rst b/NEWS.rst
index 4921b9946..2e74fd790 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -9,6 +9,25 @@
 
 .. towncrier release notes start
 
+25.1.1 (2025-05-02)
+===================
+
+Bug Fixes
+---------
+
+- Fix ``req.source_dir`` AssertionError when using the legacy resolver. (`#13353 <https://github.com/pypa/pip/issues/13353>`_)
+- Fix crash on Python 3.9.6 and lower when pip failed to compile a Python module
+  during installation. (`#13364 <https://github.com/pypa/pip/issues/13364>`_)
+- Names in dependency group includes are now normalized before lookup, which
+  fixes incorrect ``Dependency group '...' not found`` errors. (`#13372 <https://github.com/pypa/pip/issues/13372>`_)
+
+Vendored Libraries
+------------------
+
+- Fix issues with using tomllib from the stdlib if available, rather than tomli
+- Upgrade dependency-groups to 1.3.1
+
+
 25.1 (2025-04-26)
 =================
 
diff --git a/build-project/build-requirements.txt b/build-project/build-requirements.txt
index f91e236eb..79c1ac704 100644
--- a/build-project/build-requirements.txt
+++ b/build-project/build-requirements.txt
@@ -18,7 +18,7 @@ pyproject-hooks==1.2.0 \
     # via build
 
 # The following packages are considered to be unsafe in a requirements file:
-setuptools==79.0.0 \
-    --hash=sha256:9828422e7541213b0aacb6e10bbf9dd8febeaa45a48570e09b6d100e063fc9f9 \
-    --hash=sha256:b9ab3a104bedb292323f53797b00864e10e434a3ab3906813a7169e4745b912a
+setuptools==80.0.0 \
+    --hash=sha256:a38f898dcd6e5380f4da4381a87ec90bd0a7eec23d204a5552e80ee3cab6bd27 \
+    --hash=sha256:c40a5b3729d58dd749c0f08f1a07d134fb8a0a3d7f87dc33e7c5e1f762138650
     # via -r build-requirements.in
diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py
index 49b8fd78b..300ae92b3 100644
--- a/src/pip/_internal/commands/install.py
+++ b/src/pip/_internal/commands/install.py
@@ -420,7 +420,7 @@ class InstallCommand(RequirementCommand):
 
             reqs_to_build = [
                 r
-                for r in requirement_set.requirements.values()
+                for r in requirement_set.requirements_to_install
                 if should_build_for_install_command(r)
             ]
 
diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py
index 73e4bfc7c..cfc3b26eb 100644
--- a/src/pip/_internal/operations/install/wheel.py
+++ b/src/pip/_internal/operations/install/wheel.py
@@ -13,7 +13,6 @@ import sys
 import warnings
 from base64 import urlsafe_b64encode
 from email.message import Message
-from io import StringIO
 from itertools import chain, filterfalse, starmap
 from typing import (
     IO,
@@ -50,7 +49,7 @@ from pip._internal.metadata import (
 from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl
 from pip._internal.models.scheme import SCHEME_KEYS, Scheme
 from pip._internal.utils.filesystem import adjacent_tmp_file, replace
-from pip._internal.utils.misc import ensure_dir, hash_file, partition
+from pip._internal.utils.misc import StreamWrapper, ensure_dir, hash_file, partition
 from pip._internal.utils.unpacking import (
     current_umask,
     is_within_directory,
@@ -607,7 +606,9 @@ def _install_wheel(  # noqa: C901, PLR0915 function is too long
 
     # Compile all of the pyc files for the installed files
     if pycompile:
-        with contextlib.redirect_stdout(StringIO()) as stdout:
+        with contextlib.redirect_stdout(
+            StreamWrapper.from_stream(sys.stdout)
+        ) as stdout:
             with warnings.catch_warnings():
                 warnings.filterwarnings("ignore")
                 for path in pyc_source_file_paths():
diff --git a/src/pip/_internal/pyproject.py b/src/pip/_internal/pyproject.py
index 0e8452f39..f4e41c72c 100644
--- a/src/pip/_internal/pyproject.py
+++ b/src/pip/_internal/pyproject.py
@@ -1,14 +1,8 @@
 import importlib.util
 import os
-import sys
 from collections import namedtuple
 from typing import Any, List, Optional
 
-if sys.version_info >= (3, 11):
-    import tomllib
-else:
-    from pip._vendor import tomli as tomllib
-
 from pip._vendor.packaging.requirements import InvalidRequirement
 
 from pip._internal.exceptions import (
@@ -16,6 +10,7 @@ from pip._internal.exceptions import (
     InvalidPyProjectBuildRequires,
     MissingPyProjectBuildRequires,
 )
+from pip._internal.utils.compat import tomllib
 from pip._internal.utils.packaging import get_requirement
 
 
diff --git a/src/pip/_internal/req/req_dependency_group.py b/src/pip/_internal/req/req_dependency_group.py
index 8f124de5b..78102ecb6 100644
--- a/src/pip/_internal/req/req_dependency_group.py
+++ b/src/pip/_internal/req/req_dependency_group.py
@@ -1,9 +1,9 @@
 from typing import Any, Dict, Iterable, Iterator, List, Tuple
 
-from pip._vendor import tomli
 from pip._vendor.dependency_groups import DependencyGroupResolver
 
 from pip._internal.exceptions import InstallationError
+from pip._internal.utils.compat import tomllib
 
 
 def parse_dependency_groups(groups: List[Tuple[str, str]]) -> List[str]:
@@ -65,10 +65,10 @@ def _load_pyproject(path: str) -> Dict[str, Any]:
     """
     try:
         with open(path, "rb") as fp:
-            return tomli.load(fp)
+            return tomllib.load(fp)
     except FileNotFoundError:
         raise InstallationError(f"{path} not found. Cannot resolve '--group' option.")
-    except tomli.TOMLDecodeError as e:
+    except tomllib.TOMLDecodeError as e:
         raise InstallationError(f"Error parsing {path}: {e}") from e
     except OSError as e:
         raise InstallationError(f"Error reading {path}: {e}") from e
diff --git a/src/pip/_internal/utils/compat.py b/src/pip/_internal/utils/compat.py
index d8b54e4ee..324789f1d 100644
--- a/src/pip/_internal/utils/compat.py
+++ b/src/pip/_internal/utils/compat.py
@@ -7,7 +7,7 @@ import os
 import sys
 from typing import IO
 
-__all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"]
+__all__ = ["get_path_uid", "stdlib_pkgs", "tomllib", "WINDOWS"]
 
 
 logger = logging.getLogger(__name__)
@@ -67,6 +67,12 @@ else:
         )
 
 
+if sys.version_info >= (3, 11):
+    import tomllib
+else:
+    from pip._vendor import tomli as tomllib
+
+
 # packages in the stdlib that may have installation metadata, but should not be
 # considered 'installed'.  this theoretically could be determined based on
 # dist.location (py27:`sysconfig.get_paths()['stdlib']`,
diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py
index 156accda1..44f6a05fb 100644
--- a/src/pip/_internal/utils/misc.py
+++ b/src/pip/_internal/utils/misc.py
@@ -11,6 +11,7 @@ import sysconfig
 import urllib.parse
 from dataclasses import dataclass
 from functools import partial
+from io import StringIO
 from itertools import filterfalse, tee, zip_longest
 from pathlib import Path
 from types import FunctionType, TracebackType
@@ -25,6 +26,7 @@ from typing import (
     Mapping,
     Optional,
     Sequence,
+    TextIO,
     Tuple,
     Type,
     TypeVar,
@@ -373,6 +375,22 @@ def write_output(msg: Any, *args: Any) -> None:
     logger.info(msg, *args)
 
 
+class StreamWrapper(StringIO):
+    orig_stream: TextIO
+
+    @classmethod
+    def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper":
+        ret = cls()
+        ret.orig_stream = orig_stream
+        return ret
+
+    # compileall.compile_dir() needs stdout.encoding to print to stdout
+    # type ignore is because TextIOBase.encoding is writeable
+    @property
+    def encoding(self) -> str:  # type: ignore
+        return self.orig_stream.encoding
+
+
 # Simulates an enum
 def enum(*sequential: Any, **named: Any) -> Type[Any]:
     enums = dict(zip(sequential, range(len(sequential))), **named)
diff --git a/src/pip/_vendor/dependency_groups/_implementation.py b/src/pip/_vendor/dependency_groups/_implementation.py
index 80d916938..64e314a63 100644
--- a/src/pip/_vendor/dependency_groups/_implementation.py
+++ b/src/pip/_vendor/dependency_groups/_implementation.py
@@ -12,7 +12,7 @@ def _normalize_name(name: str) -> str:
 
 
 def _normalize_group_names(
-    dependency_groups: Mapping[str, str | Mapping[str, str]]
+    dependency_groups: Mapping[str, str | Mapping[str, str]],
 ) -> Mapping[str, str | Mapping[str, str]]:
     original_names: dict[str, list[str]] = {}
     normalized_groups = {}
@@ -171,17 +171,16 @@ class DependencyGroupResolver:
             if isinstance(item, Requirement):
                 resolved_group.append(item)
             elif isinstance(item, DependencyGroupInclude):
-                if item.include_group in self._include_graph_ancestors.get(group, ()):
+                include_group = _normalize_name(item.include_group)
+                if include_group in self._include_graph_ancestors.get(group, ()):
                     raise CyclicDependencyError(
                         requested_group, group, item.include_group
                     )
-                self._include_graph_ancestors[item.include_group] = (
+                self._include_graph_ancestors[include_group] = (
                     *self._include_graph_ancestors.get(group, ()),
                     group,
                 )
-                resolved_group.extend(
-                    self._resolve(item.include_group, requested_group)
-                )
+                resolved_group.extend(self._resolve(include_group, requested_group))
             else:  # unreachable
                 raise NotImplementedError(
                     f"Invalid dependency group item after parse: {item}"
@@ -206,8 +205,5 @@ def resolve(
     :raises LookupError: if group name is absent
     :raises packaging.requirements.InvalidRequirement: if a specifier is not valid
     """
-    return tuple(
-        str(r)
-        for group in groups
-        for r in DependencyGroupResolver(dependency_groups).resolve(group)
-    )
+    resolver = DependencyGroupResolver(dependency_groups)
+    return tuple(str(r) for group in groups for r in resolver.resolve(group))
diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt
index 283d57f5f..b6597dc00 100644
--- a/src/pip/_vendor/vendor.txt
+++ b/src/pip/_vendor/vendor.txt
@@ -17,4 +17,4 @@ setuptools==70.3.0
 tomli==2.2.1
 tomli-w==1.2.0
 truststore==0.10.1
-dependency-groups==1.3.0
+dependency-groups==1.3.1


Test patch:
diff --git a/tests/functional/test_lock.py b/tests/functional/test_lock.py
index 861fc87b6..b99850526 100644
--- a/tests/functional/test_lock.py
+++ b/tests/functional/test_lock.py
@@ -1,16 +1,11 @@
-import sys
 import textwrap
 from pathlib import Path
 
+from pip._internal.utils.compat import tomllib
 from pip._internal.utils.urls import path_to_url
 
 from ..lib import PipTestEnvironment, TestData
 
-if sys.version_info >= (3, 11):
-    import tomllib
-else:
-    from pip._vendor import tomli as tomllib
-
 
 def test_lock_wheel_from_findlinks(
     script: PipTestEnvironment, shared_data: TestData, tmp_path: Path
diff --git a/tests/requirements-common_wheels.txt b/tests/requirements-common_wheels.txt
index 6403ed738..31450a0e8 100644
--- a/tests/requirements-common_wheels.txt
+++ b/tests/requirements-common_wheels.txt
@@ -5,7 +5,9 @@
 # 4. Replacing the `setuptools` entry below with a `file:///...` URL
 # (Adjust artifact directory used based on preference and operating system)
 
-setuptools >= 40.8.0, != 60.6.0
+# We pin setuptools<80 because our test suite currently
+# depends on setup.py develop to generate egg-link files.
+setuptools >= 40.8.0, != 60.6.0, <80
 wheel
 # As required by pytest-cov.
 coverage >= 4.4
diff --git a/tests/unit/test_req_dependency_group.py b/tests/unit/test_req_dependency_group.py
index b596f6fc5..1b180f8d7 100644
--- a/tests/unit/test_req_dependency_group.py
+++ b/tests/unit/test_req_dependency_group.py
@@ -120,7 +120,7 @@ def test_parse_gets_unexpected_oserror(
         raise OSError(errno.EPIPE, "Broken pipe")
 
     monkeypatch.setattr(
-        "pip._internal.req.req_dependency_group.tomli.load", epipe_toml_load
+        "pip._internal.req.req_dependency_group.tomllib.load", epipe_toml_load
     )
 
     with pytest.raises(InstallationError, match=r"Error reading pyproject\.toml"):