Skip to content

Commit

Permalink
Add -c/--constraint option to pip-compile (#1936)
Browse files Browse the repository at this point in the history
  • Loading branch information
atugushev authored Jul 27, 2023
1 parent d7885f8 commit b55abdc
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 5 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ dependencies = ["django"]

[project.optional-dependencies]
dev = ["pytest"]

```

You can produce your pin files as easily as:
Expand Down
27 changes: 27 additions & 0 deletions piptools/scripts/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,20 @@ def _determine_linesep(
help="Do not read any config file.",
is_eager=True,
)
@click.option(
"-c",
"--constraint",
type=click.Path(
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
allow_dash=False,
path_type=str,
),
multiple=True,
help="Constrain versions using the given constraints file; may be used more than once.",
)
def cli(
ctx: click.Context,
verbose: int,
Expand Down Expand Up @@ -366,6 +380,7 @@ def cli(
unsafe_package: tuple[str, ...],
config: Path | None,
no_config: bool,
constraint: tuple[str, ...],
) -> None:
"""
Compiles requirements.txt from requirements.in, pyproject.toml, setup.cfg,
Expand Down Expand Up @@ -561,6 +576,18 @@ def cli(
)
)

# Parse all constraints from `--constraint` files
for filename in constraint:
constraints.extend(
parse_requirements(
filename,
constraint=True,
finder=repository.finder,
options=repository.options,
session=repository.session,
)
)

if upgrade_packages:
constraints_file = tempfile.NamedTemporaryFile(mode="wt", delete=False)
constraints_file.write("\n".join(upgrade_packages))
Expand Down
8 changes: 4 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from functools import partial
from pathlib import Path
from textwrap import dedent
from typing import Any
from typing import Any, cast

import pytest
import tomli_w
Expand Down Expand Up @@ -234,7 +234,7 @@ def runner():
@pytest.fixture
def tmpdir_cwd(tmpdir):
with tmpdir.as_cwd():
yield tmpdir
yield Path(tmpdir)


@pytest.fixture
Expand Down Expand Up @@ -455,12 +455,12 @@ def _maker(
pyproject_param: str, new_default: Any, config_file_name: str = CONFIG_FILE_NAME
) -> Path:
# Make a config file with this one config default override
config_path = Path(tmpdir_cwd) / pyproject_param
config_path = tmpdir_cwd / pyproject_param
config_file = config_path / config_file_name
config_path.mkdir(exist_ok=True)

config_to_dump = {"tool": {"pip-tools": {pyproject_param: new_default}}}
config_file.write_text(tomli_w.dumps(config_to_dump))
return config_file.relative_to(tmpdir_cwd)
return cast(Path, config_file.relative_to(tmpdir_cwd))

return _maker
32 changes: 32 additions & 0 deletions tests/test_cli_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3019,6 +3019,38 @@ def test_raise_error_on_invalid_config_option(
assert "Invalid value for config key 'dry_run': ['invalid', 'value']" in out.stderr


@pytest.mark.parametrize("option", ("-c", "--constraint"))
def test_constraint_option(pip_conf, runner, tmpdir_cwd, make_config_file, option):
req_in = tmpdir_cwd / "requirements.in"
req_in.write_text("small-fake-a")

constraints_txt = tmpdir_cwd / "constraints.txt"
constraints_txt.write_text("small-fake-a==0.1")

out = runner.invoke(
cli,
[
req_in.name,
option,
constraints_txt.name,
"--output-file",
"-",
"--no-header",
"--no-emit-options",
],
)

assert out.exit_code == 0
assert out.stdout == dedent(
"""\
small-fake-a==0.1
# via
# -c constraints.txt
# -r requirements.in
"""
)


def test_allow_in_config_pip_sync_option(pip_conf, runner, tmp_path, make_config_file):
config_file = make_config_file("--ask", True) # pip-sync's option

Expand Down
1 change: 1 addition & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ def test_callback_config_file_defaults(pyproject_param, new_default, make_config
("trusted_host", "not-a-list"),
("annotate", "not-a-bool"),
("max_rounds", "not-an-int"),
("constraint", "not-an-list"),
),
)
def test_callback_config_file_defaults_multi_validate_value(
Expand Down

0 comments on commit b55abdc

Please sign in to comment.