diff --git a/docs/docs/cli.md b/docs/docs/cli.md index f0b2989a9ad..45881bec80a 100644 --- a/docs/docs/cli.md +++ b/docs/docs/cli.md @@ -272,7 +272,14 @@ poetry add ../my-package/dist/my-package-0.1.0.tar.gz poetry add ../my-package/dist/my_package-0.1.0.whl ``` -If you want the dependency to be installed in editable mode you can specify it in the `pyproject.toml` file. It means that changes in the local directory will be reflected directly in environment. +If you want the dependency to be installed in editable mode you can use the `--editable` option. + +```bash +poetry add --editable ./my-package/ +poetry add --editable git+ssh://github.com/sdispater/pendulum.git#develop +``` + +Alternatively, you can specify it in the `pyproject.toml` file. It means that changes in the local directory will be reflected directly in environment. ```toml [tool.poetry.dependencies] @@ -296,6 +303,7 @@ poetry add "git+https://github.com/pallets/flask.git@1.1.1[dotenv,dev]" ### Options * `--dev (-D)`: Add package as development dependency. +* `--editable (-e)`: Add vcs/path dependencies as editable. * `--extras (-E)`: Extras to activate for the dependency. (multiple values allowed) * `--optional`: Add as an optional dependency. * `--python`: Python version for which the dependency must be installed. diff --git a/poetry/console/commands/add.py b/poetry/console/commands/add.py index adfe4d9f062..777f3744787 100644 --- a/poetry/console/commands/add.py +++ b/poetry/console/commands/add.py @@ -17,6 +17,7 @@ class AddCommand(InstallerCommand, InitCommand): arguments = [argument("name", "The packages to add.", multiple=True)] options = [ option("dev", "D", "Add as a development dependency."), + option("editable", "e", "Add vcs/path dependencies as editable."), option( "extras", "E", @@ -139,6 +140,19 @@ def handle(self) -> int: constraint["extras"] = self.option("extras") + if self.option("editable"): + if "git" in _constraint or "path" in _constraint: + constraint["develop"] = True + else: + self.line_error( + "\n" + "Failed to add packages. " + "Only vcs/path dependencies support editable installs. " + f"{_constraint['name']} is neither." + ) + self.line_error("\nNo changes were applied.") + return 1 + if self.option("python"): constraint["python"] = self.option("python") diff --git a/tests/console/commands/test_add.py b/tests/console/commands/test_add.py index 4808c53f29f..26955345457 100644 --- a/tests/console/commands/test_add.py +++ b/tests/console/commands/test_add.py @@ -50,6 +50,24 @@ def test_add_no_constraint(app, repo, tester): assert content["dependencies"]["cachy"] == "^0.2.0" +def test_add_no_constraint_editable_error(app, repo, tester): + content = app.poetry.file.read()["tool"]["poetry"] + + repo.add_package(get_package("cachy", "0.2.0")) + + tester.execute("-e cachy") + + expected = """ +Failed to add packages. Only vcs/path dependencies support editable installs. cachy is neither. + +No changes were applied. +""" + assert 1 == tester.status_code + assert expected == tester.io.fetch_error() + assert 0 == tester.command.installer.executor.installations_count + assert content == app.poetry.file.read()["tool"]["poetry"] + + def test_add_equal_constraint(app, repo, tester): repo.add_package(get_package("cachy", "0.1.0")) repo.add_package(get_package("cachy", "0.2.0")) @@ -243,13 +261,15 @@ def test_add_git_constraint_with_extras(app, repo, tester, tmp_venv): } -def test_add_git_ssh_constraint(app, repo, tester, tmp_venv): +@pytest.mark.parametrize("editable", [False, True]) +def test_add_git_ssh_constraint(editable, app, repo, tester, tmp_venv): tester.command.set_env(tmp_venv) repo.add_package(get_package("pendulum", "1.4.4")) repo.add_package(get_package("cleo", "0.6.5")) - tester.execute("git+ssh://git@github.com/demo/demo.git@develop") + url = "git+ssh://git@github.com/demo/demo.git@develop" + tester.execute(f"{url}" if not editable else f"-e {url}") expected = """\ @@ -270,13 +290,19 @@ def test_add_git_ssh_constraint(app, repo, tester, tmp_venv): content = app.poetry.file.read()["tool"]["poetry"] assert "demo" in content["dependencies"] - assert content["dependencies"]["demo"] == { + + expected = { "git": "ssh://git@github.com/demo/demo.git", "rev": "develop", } + if editable: + expected["develop"] = True + + assert content["dependencies"]["demo"] == expected -def test_add_directory_constraint(app, repo, tester, mocker): +@pytest.mark.parametrize("editable", [False, True]) +def test_add_directory_constraint(editable, app, repo, tester, mocker): p = mocker.patch("pathlib.Path.cwd") p.return_value = Path(__file__).parent @@ -284,7 +310,7 @@ def test_add_directory_constraint(app, repo, tester, mocker): repo.add_package(get_package("cleo", "0.6.5")) path = "../git/github.com/demo/demo" - tester.execute("{}".format(path)) + tester.execute(f"{path}" if not editable else f"-e {path}") expected = """\ @@ -307,7 +333,12 @@ def test_add_directory_constraint(app, repo, tester, mocker): content = app.poetry.file.read()["tool"]["poetry"] assert "demo" in content["dependencies"] - assert content["dependencies"]["demo"] == {"path": "../git/github.com/demo/demo"} + + expected = {"path": "../git/github.com/demo/demo"} + if editable: + expected["develop"] = True + + assert content["dependencies"]["demo"] == expected def test_add_directory_with_poetry(app, repo, tester, mocker):