Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2e4086e
Implement build constraints
notatallshaw Aug 9, 2025
1aa1d32
Add build constraints tests
notatallshaw Aug 9, 2025
db4fcf7
Add build constraints to user guide
notatallshaw Aug 9, 2025
d9d5f5d
NEWS ENTRY
notatallshaw Aug 9, 2025
8d170b5
Imply using new behavior when build constraints are provided without …
notatallshaw Aug 9, 2025
9f9032c
Update src/pip/_internal/cli/req_command.py
notatallshaw Aug 14, 2025
a9e81d7
Update src/pip/_internal/build_env.py
notatallshaw Aug 14, 2025
4fbafeb
Merge branch 'main' into add-build-constraints
notatallshaw Aug 14, 2025
8943172
Fix linting
notatallshaw Aug 19, 2025
ef06010
Fix test
notatallshaw Aug 19, 2025
d564457
Consistently use "build constraints" in variables and documentation
notatallshaw Aug 19, 2025
ebd55e7
Simplify deprecation warning
notatallshaw Aug 19, 2025
c41496e
Only emit pip constraint deprecation warning once
notatallshaw Aug 19, 2025
e015f3d
Move `ExtraEnviron` into type checking block
notatallshaw Aug 19, 2025
b333b85
Use standard `assert_installed` in functional tests for build constra…
notatallshaw Aug 20, 2025
bc48f0b
Eagerly assert build constraints files
notatallshaw Aug 20, 2025
fc1bfb5
Add deprecation news item.
notatallshaw Aug 20, 2025
74b08e1
Remove pointless check for `_PIP_IN_BUILD_IGNORE_CONSTRAINTS` in `_de…
notatallshaw Aug 20, 2025
41164aa
Exit `_deprecation_constraint_check` early when build constraints pre…
notatallshaw Aug 20, 2025
e53db93
Remove superfluous `constraints` parameter
notatallshaw Aug 21, 2025
f372c74
Merge branch 'main' into add-build-constraints
notatallshaw Aug 29, 2025
d86d520
Merge branch 'main' into add-build-constraints
notatallshaw Sep 8, 2025
05aeb84
Merge branch 'main' into add-build-constraints
notatallshaw Sep 19, 2025
4c04652
Merge branch 'main' into add-build-constraints
notatallshaw Sep 25, 2025
ab36b15
Merge branch 'main' into add-build-constraints
notatallshaw Oct 5, 2025
d8f372c
Use with to close pip session.
notatallshaw Oct 11, 2025
d0cf197
Remove deprication supression logic
notatallshaw Oct 11, 2025
b091be2
Update tests
notatallshaw Oct 11, 2025
aa5fcd9
Merge branch 'main' into add-build-constraints
notatallshaw Oct 11, 2025
d0bcf5c
fix lint
notatallshaw Oct 11, 2025
11676b0
Merge branch 'main' into add-build-constraints
notatallshaw Oct 14, 2025
035396d
Merge branch 'main' into add-build-constraints
notatallshaw Oct 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Imply using new behavior when build constraints are provided without …
…needing to use the `build-constraint` feature
  • Loading branch information
notatallshaw committed Aug 9, 2025
commit 8d170b56915c76345e63f4a5f5c390213badc825
24 changes: 10 additions & 14 deletions docs/html/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -262,20 +262,16 @@ serve them in a centralized place.
Build Constraints
-----------------

.. versionadded:: 25.2
.. note::

Build constraints are currently an **experimental feature** and must be
enabled using ``--use-feature=build-constraint``.
.. versionadded:: 25.3

Build constraints are a specialized type of constraints file that apply only
to the build environment when building packages from source. Unlike regular
constraints which affect the installed packages in your environment, build
Build constraints are a type of constraints file that applies only to isolated
build environments used for building packages from source. Unlike regular
constraints, which affect the packages installed in your environment, build
constraints only influence the versions of packages available during the
build process.

This is particularly useful when you need to constrain build dependencies
(like ``setuptools``, ``cython``, etc.) without affecting the
This is useful when you need to constrain build dependencies
(such as ``setuptools``, ``cython``, etc.) without affecting the
final installed environment.

Use build constraints like so:
Expand All @@ -284,21 +280,21 @@ Use build constraints like so:

.. code-block:: shell

python -m pip install --build-constraint build-constraints.txt --use-feature=build-constraint SomePackage
python -m pip install --build-constraint build-constraints.txt SomePackage

.. tab:: Windows

.. code-block:: shell

py -m pip install --build-constraint build-constraints.txt --use-feature=build-constraint SomePackage
py -m pip install --build-constraint build-constraints.txt SomePackage

Example build constraints file (``build-constraints.txt``):

.. code-block:: text

# Constrain setuptools version during build
setuptools>=45.0.0,<60.0.0
# Pin Cython for packages that use it
setuptools>=45,<80
# Pin Cython for packages that use it to build
cython==0.29.24


Expand Down
12 changes: 8 additions & 4 deletions news/13534.feature.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
Add experimental build constraints support via ``--use-feature=build-constraint``.
This allows constraining the versions of packages used during the build process
(e.g., setuptools). Build constraints can be specified via ``PIP_BUILD_CONSTRAINT``
environment variable or ``--build-constraint`` flag.
Add support for build constraints via the ``--build-constraint`` option. This
allows constraining the versions of packages used during the build process
(e.g., setuptools).

When using ``--build-constraint``, you can no longer pass constraints to
isolated build environments via the ``PIP_CONSTRAINT`` environment variable.
To opt in to this behavior without specifying any build constraints, use
``--use-feature=build-constraint``.
28 changes: 18 additions & 10 deletions src/pip/_internal/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,23 +128,29 @@ def _deprecation_constraint_check(self) -> None:
if self._build_constraint_feature_enabled:
return

if self._build_constraints:
return

if not self._constraints:
return

if not os.environ.get("PIP_CONSTRAINT"):
return

pip_constraint_files = [
f.strip() for f in os.environ["PIP_CONSTRAINT"].split() if f.strip()
f for f in os.environ["PIP_CONSTRAINT"].split() if f.strip()
]
if pip_constraint_files and set(pip_constraint_files) == set(self._constraints):
if pip_constraint_files and pip_constraint_files == self._constraints:
deprecated(
reason=(
"Setting PIP_CONSTRAINT will not affect "
"build constraints in the future,"
),
replacement=(
'PIP_BUILD_CONSTRAINT with PIP_USE_FEATURE="build-constraint"'
"to specify build constraints use --build-constraint or "
"PIP_BUILD_CONSTRAINT, to disable this warning without "
"any build constraints set --use-feature=build-constraint or "
'PIP_USE_FEATURE="build-constraint"'
),
gone_in="26.2",
issue=None,
Expand Down Expand Up @@ -219,19 +225,21 @@ def install(
# Handle build constraints
extra_environ: ExtraEnviron = {}
if self._build_constraint_feature_enabled:
args.extend(["--use-feature", "build-constraint"])

if self._build_constraints:
# Build constraints must be passed as both constraints
# and build constraints to the subprocess
# and build constraints, so that nested builds receive
# build constraints
for constraint_file in self._build_constraints:
args.extend(["--constraint", constraint_file])
args.extend(["--build-constraint", constraint_file])
args.extend(["--use-feature", "build-constraint"])

if self._build_constraint_feature_enabled and not self._build_constraints:
# If there are no build constraints but the build constraint
# process is enabled then we must ignore regular constraints
if not self._build_constraints:
extra_environ = {
"extra_environ": {"_PIP_IN_BUILD_IGNORE_CONSTRAINTS": "1"}
}
# feature is enabled then we must ignore regular constraints
# in the isolated build environment
extra_environ = {"extra_environ": {"_PIP_IN_BUILD_IGNORE_CONSTRAINTS": "1"}}

args.append("--")
args.extend(requirements)
Expand Down
8 changes: 1 addition & 7 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,6 @@ def check_build_constraints(options: Values) -> None:
:param options: The OptionParser options.
"""
if hasattr(options, "build_constraints") and options.build_constraints:
if "build-constraint" not in options.features_enabled:
raise CommandError(
"To use --build-constraint, you must enable this feature with "
"--use-feature=build-constraint."
)
if not options.build_isolation:
raise CommandError(
"--build-constraint cannot be used with --no-build-isolation."
Expand Down Expand Up @@ -457,8 +452,7 @@ def build_constraint() -> Option:
metavar="file",
help=(
"Constrain build dependencies using the given constraints file. "
"This option can be used multiple times. "
"Requires --use-feature=build-constraint."
"This option can be used multiple times."
),
)

Expand Down
8 changes: 4 additions & 4 deletions tests/functional/test_build_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ def _run_pip_install_with_build_constraints_no_feature_flag(
"--build-constraint",
str(constraints_file),
str(project_dir),
expect_error=True,
)


Expand Down Expand Up @@ -164,13 +163,14 @@ def test_build_constraints_file_not_found(
def test_build_constraints_without_feature_flag(
script: PipTestEnvironment, tmpdir: Path
) -> None:
"""Test that --build-constraint requires the feature flag."""
"""Test that --build-constraint automatically enables the feature."""
project_dir = _create_simple_test_package(script=script, name="test_no_feature")
constraints_file = _create_constraints_file(
script=script, filename="constraints.txt", content="setuptools==45.0.0\n"
)
result = _run_pip_install_with_build_constraints_no_feature_flag(
script=script, project_dir=project_dir, constraints_file=constraints_file
)
assert result.returncode != 0
assert "build-constraint" in result.stderr.lower()
# Should succeed now that --build-constraint auto-enables the feature
assert result.returncode == 0
_assert_successful_installation(result=result, package_name="test_no_feature")
18 changes: 4 additions & 14 deletions tests/unit/test_build_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ def test_deprecation_check_warning_shown(self) -> None:
"Setting PIP_CONSTRAINT will not affect build constraints in the future"
in message
)
assert 'PIP_BUILD_CONSTRAINT with PIP_USE_FEATURE="build-constraint"' in message
assert (
"to specify build constraints use "
"--build-constraint or PIP_BUILD_CONSTRAINT" in message
)

@mock.patch.dict(os.environ, {"PIP_CONSTRAINT": "constraint1.txt constraint2.txt"})
def test_deprecation_check_multiple_constraints(self) -> None:
Expand All @@ -96,19 +99,6 @@ def test_deprecation_check_multiple_constraints(self) -> None:
with pytest.warns(PipDeprecationWarning):
installer._deprecation_constraint_check()

@mock.patch.dict(os.environ, {"PIP_CONSTRAINT": "constraint1.txt constraint2.txt"})
def test_deprecation_check_multiple_constraints_different_order(self) -> None:
"""Test deprecation warning works when constraints are in different order."""
finder = make_test_finder()
installer = SubprocessBuildEnvironmentInstaller(
finder,
constraints=["constraint2.txt", "constraint1.txt"],
build_constraint_feature_enabled=False,
)

with pytest.warns(PipDeprecationWarning):
installer._deprecation_constraint_check()

@mock.patch.dict(
os.environ, {"PIP_CONSTRAINT": "constraint1.txt constraint2.txt extra.txt"}
)
Expand Down
Loading