Skip to content

Commit

Permalink
feat(venv): add option to show the path to a venv (#1680)
Browse files Browse the repository at this point in the history
  • Loading branch information
frostming committed Mar 13, 2023
1 parent ba3c22d commit 5b52052
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 3 deletions.
7 changes: 7 additions & 0 deletions docs/docs/usage/venv.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ Virtualenvs created with this project:
- 3.9.1: C:\Users\Frost Ming\AppData\Local\pdm\pdm\venvs\test-project-8Sgn_62n-3.9.1
```

## Show the path or python interpreter of a virtualenv

```bash
$ pdm venv --path for-test
$ pdm venv --python for-test
```

## Remove a virtualenv

```bash
Expand Down
1 change: 1 addition & 0 deletions news/1680.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add new options to `venv` command to show the path or the python interpreter for a managed venv.
29 changes: 28 additions & 1 deletion src/pdm/cli/commands/venv/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

import argparse
from pathlib import Path
from pdm.cli.commands.venv.utils import iter_venvs, get_venv_python

from pdm.exceptions import PdmUsageError
from pdm.project import Project
from pdm.cli.commands.base import BaseCommand
from pdm.cli.commands.venv.activate import ActivateCommand
Expand All @@ -19,6 +22,10 @@ class Command(BaseCommand):
arguments: list[Option] = []

def add_arguments(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument("--path", help="Show the path to the given virtualenv")
parser.add_argument(
"--python", help="Show the python interpreter path for the given virtualenv"
)
subparser = parser.add_subparsers()
CreateCommand.register_to(subparser, "create")
ListCommand.register_to(subparser, "list")
Expand All @@ -27,5 +34,25 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
PurgeCommand.register_to(subparser, "purge")
self.parser = parser

def _get_venv_with_name(self, project: Project, name: str) -> Path:
venv = next((venv for key, venv in iter_venvs(project) if key == name), None)
if not venv:
project.core.ui.echo(
f"No virtualenv with key [success]{name}[/] is found",
style="warning",
err=True,
)
raise SystemExit(1)
return venv

def handle(self, project: Project, options: argparse.Namespace) -> None:
self.parser.print_help()
if options.path and options.python:
raise PdmUsageError("--path and --python are mutually exclusive")
if options.path:
venv = self._get_venv_with_name(project, options.path)
project.core.ui.echo(str(venv))
elif options.python:
venv = self._get_venv_with_name(project, options.python)
project.core.ui.echo(str(get_venv_python(venv)))
else:
self.parser.print_help()
20 changes: 18 additions & 2 deletions tests/cli/test_venv.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ def with_pip(request):

@pytest.fixture()
def fake_create(monkeypatch):
def fake_create(self, location, *args, prompt=None):
location.mkdir(parents=True)
def fake_create(self, location, *args):
bin_dir = "Scripts" if sys.platform == "win32" else "bin"
suffix = ".exe" if sys.platform == "win32" else ""
(location / bin_dir).mkdir(parents=True)
(location / bin_dir / f"python{suffix}").touch()

monkeypatch.setattr(backends.VirtualenvBackend, "perform_create", fake_create)
monkeypatch.setattr(backends.VenvBackend, "perform_create", fake_create)
Expand Down Expand Up @@ -47,6 +50,19 @@ def test_venv_create_in_project(invoke, project):
assert "is not empty" in result.stderr


@pytest.mark.usefixtures("fake_create")
def test_venv_show_path(invoke, project):
project.project_config["venv.in_project"] = True
invoke(["venv", "create"], obj=project, strict=True)
invoke(["venv", "create", "--name", "test"], obj=project, strict=True)
result = invoke(["venv", "--path", "in-project"], obj=project, strict=True)
assert result.output.strip() == str(project.root / ".venv")
result = invoke(["venv", "--path", "test"], obj=project)
assert result.exit_code == 0
result = invoke(["venv", "--path", "foo"], obj=project)
assert result.exit_code == 1


@pytest.mark.usefixtures("fake_create")
def test_venv_list(invoke, project):
project.project_config["venv.in_project"] = False
Expand Down

0 comments on commit 5b52052

Please sign in to comment.