Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix passing constraints file path into pipx install operation via --pip-args #1390

Merged
merged 4 commits into from
Jun 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions changelog.d/1389.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix passing constraints file path into `pipx install` operation via `pip` args
4 changes: 3 additions & 1 deletion src/pipx/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ def get_pip_args(parsed_args: Dict[str, str]) -> List[str]:
pip_args += ["--index-url", parsed_args["index_url"]]

if parsed_args.get("pip_args"):
pip_args += shlex.split(parsed_args.get("pip_args", ""), posix=not WINDOWS)
# Stripping the single quote that can be parsed from several shells
pip_args_striped = parsed_args["pip_args"].strip("'")
pip_args += shlex.split(pip_args_striped, posix=not WINDOWS)

# make sure --editable is last because it needs to be right before
# package specification
Expand Down
22 changes: 21 additions & 1 deletion src/pipx/package_specifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,27 @@ def parse_specifier_for_install(package_spec: str, pip_args: List[str]) -> Tuple
)
pip_args.remove("--editable")

return (package_or_url, pip_args)
for index, option in enumerate(pip_args):
if not option.startswith(("-c", "--constraint")):
continue

if option in ("-c", "--constraint"):
argument_index = index + 1
if argument_index < len(pip_args):
constraints_file = pip_args[argument_index]
pip_args[argument_index] = str(Path(constraints_file).expanduser().resolve())

else: # option == "--constraint=some_path"
option_list = option.split("=")

if len(option_list) == 2:
key, value = option_list
value_path = Path(value).expanduser().resolve()
pip_args[index] = f"{key}={value_path}"

break

return package_or_url, pip_args


def parse_specifier_for_metadata(package_spec: str) -> str:
Expand Down
33 changes: 33 additions & 0 deletions tests/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,39 @@ def test_pip_args_with_windows_path(pipx_temp_env, capsys):
assert r"D:\\TEST\\DIR" in captured.err


@pytest.mark.parametrize("constraint_flag", ["-c ", "--constraint ", "--constraint="])
def test_pip_args_with_constraint_relative_path(constraint_flag, pipx_temp_env, tmp_path, caplog):
constraint_file_name = "constraints.txt"
package_name = "ipython"
package_version = "8.23.0"

os.chdir(tmp_path)
chrysle marked this conversation as resolved.
Show resolved Hide resolved
constraints_file = tmp_path / constraint_file_name
constraints_file.write_text(f"{package_name}!={package_version}")
constraints_file.touch()

assert not run_pipx_cli(["install", f"--pip-args='{constraint_flag}{constraint_file_name}'", package_name])

assert f"{constraint_flag}{constraints_file}" in caplog.text

subprocess_package_version = subprocess.run([package_name, "--version"], capture_output=True, text=True, check=False)
subprocess_package_version_output = subprocess_package_version.stdout.strip()
assert subprocess_package_version_output != package_version


@pytest.mark.parametrize("constraint_flag", ["-c ", "--constraint ", "--constraint="])
def test_pip_args_with_wrong_constraint_fail(constraint_flag, pipx_ultra_temp_env, tmp_path, capsys):
constraint_file_name = "constraints.txt"
os.chdir(tmp_path)

assert run_pipx_cli(["install", f"--pip-args='{constraint_flag}{constraint_file_name}'", "pycowsay"])

assert (
f"ERROR: Could not open requirements file: [Errno 2] No such file or directory: '{constraint_file_name}'"
in capsys.readouterr().err
)


def test_install_suffix(pipx_temp_env, capsys):
name = "pbr"

Expand Down