diff --git a/docs/cli.md b/docs/cli.md
index 5d5717962a4..7ae1b7d7968 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -29,7 +29,8 @@ then `--help` combined with any of those can give you more information.
* `--no-interaction (-n)`: Do not ask any interactive question.
* `--no-plugins`: Disables plugins.
* `--no-cache`: Disables Poetry source caches.
-* `--directory=DIRECTORY (-C)`: The working directory for the Poetry command (defaults to the current working directory).
+* `--directory=DIRECTORY (-C)`: The working directory for the Poetry command (defaults to the current working directory). All command-line arguments will be resolved relative to the given directory.
+* `--project=PROJECT (-P)`: Specify another path as the project root. All command-line arguments will be resolved relative to the current working directory or directory specified using `--directory` option if used.
## new
diff --git a/src/poetry/console/application.py b/src/poetry/console/application.py
index c038db7f02b..e2d79c449ae 100644
--- a/src/poetry/console/application.py
+++ b/src/poetry/console/application.py
@@ -20,6 +20,7 @@
from poetry.__version__ import __version__
from poetry.console.command_loader import CommandLoader
from poetry.console.commands.command import Command
+from poetry.utils.helpers import directory
if TYPE_CHECKING:
@@ -110,6 +111,64 @@ def __init__(self) -> None:
command_loader = CommandLoader({name: load_command(name) for name in COMMANDS})
self.set_command_loader(command_loader)
+ @property
+ def _default_definition(self) -> Definition:
+ from cleo.io.inputs.option import Option
+
+ definition = super()._default_definition
+
+ definition.add_option(
+ Option("--no-plugins", flag=True, description="Disables plugins.")
+ )
+
+ definition.add_option(
+ Option(
+ "--no-cache", flag=True, description="Disables Poetry source caches."
+ )
+ )
+
+ definition.add_option(
+ Option(
+ "--project",
+ "-P",
+ flag=False,
+ description=(
+ "Specify another path as the project root."
+ " All command-line arguments will be resolved relative to the current working directory."
+ ),
+ )
+ )
+
+ definition.add_option(
+ Option(
+ "--directory",
+ "-C",
+ flag=False,
+ description=(
+ "The working directory for the Poetry command (defaults to the"
+ " current working directory). All command-line arguments will be"
+ " resolved relative to the given directory."
+ ),
+ )
+ )
+
+ return definition
+
+ @cached_property
+ def _project_directory(self) -> Path:
+ if self._io and self._io.input.option("project"):
+ with directory(self._working_directory):
+ return Path(self._io.input.option("project")).absolute()
+
+ return self._working_directory
+
+ @cached_property
+ def _working_directory(self) -> Path:
+ if self._io and self._io.input.option("directory"):
+ return Path(self._io.input.option("directory")).absolute()
+
+ return Path.cwd()
+
@property
def poetry(self) -> Poetry:
from poetry.factory import Factory
@@ -118,7 +177,7 @@ def poetry(self) -> Poetry:
return self._poetry
self._poetry = Factory().create_poetry(
- cwd=self._directory,
+ cwd=self._project_directory,
io=self._io,
disable_plugins=self._disable_plugins,
disable_cache=self._disable_cache,
@@ -171,7 +230,9 @@ def _run(self, io: IO) -> int:
self._load_plugins(io)
- exit_code: int = super()._run(io)
+ with directory(self._working_directory):
+ exit_code: int = super()._run(io)
+
return exit_code
def _configure_io(self, io: IO) -> None:
@@ -331,49 +392,13 @@ def _load_plugins(self, io: IO) -> None:
from poetry.plugins.application_plugin import ApplicationPlugin
from poetry.plugins.plugin_manager import PluginManager
- PluginManager.add_project_plugin_path(self._directory)
+ PluginManager.add_project_plugin_path(self._project_directory)
manager = PluginManager(ApplicationPlugin.group)
manager.load_plugins()
manager.activate(self)
self._plugins_loaded = True
- @property
- def _default_definition(self) -> Definition:
- from cleo.io.inputs.option import Option
-
- definition = super()._default_definition
-
- definition.add_option(
- Option("--no-plugins", flag=True, description="Disables plugins.")
- )
-
- definition.add_option(
- Option(
- "--no-cache", flag=True, description="Disables Poetry source caches."
- )
- )
-
- definition.add_option(
- Option(
- "--directory",
- "-C",
- flag=False,
- description=(
- "The working directory for the Poetry command (defaults to the"
- " current working directory)."
- ),
- )
- )
-
- return definition
-
- @cached_property
- def _directory(self) -> Path:
- if self._io and self._io.input.option("directory"):
- return Path(self._io.input.option("directory")).absolute()
- return Path.cwd()
-
def main() -> int:
exit_code: int = Application().run()
diff --git a/src/poetry/console/commands/init.py b/src/poetry/console/commands/init.py
index 61a44178590..9aef36f590a 100644
--- a/src/poetry/console/commands/init.py
+++ b/src/poetry/console/commands/init.py
@@ -74,12 +74,10 @@ def handle(self) -> int:
project_path = Path.cwd()
- if self.io.input.option("directory"):
- project_path = Path(self.io.input.option("directory"))
+ if self.io.input.option("project"):
+ project_path = Path(self.io.input.option("project"))
if not project_path.exists() or not project_path.is_dir():
- self.line_error(
- "The --directory path is not a directory."
- )
+ self.line_error("The --project path is not a directory.")
return 1
return self._init_pyproject(project_path=project_path)
diff --git a/src/poetry/console/commands/new.py b/src/poetry/console/commands/new.py
index bb0fefad961..d1083216329 100644
--- a/src/poetry/console/commands/new.py
+++ b/src/poetry/console/commands/new.py
@@ -54,9 +54,9 @@ class NewCommand(InitCommand):
def handle(self) -> int:
from pathlib import Path
- if self.io.input.option("directory"):
+ if self.io.input.option("project"):
self.line_error(
- "--directory only makes sense with existing projects, and will"
+ "--project only makes sense with existing projects, and will"
" be ignored. You should consider the option --path instead."
)
diff --git a/tests/console/commands/test_build.py b/tests/console/commands/test_build.py
index 3d17d030b3a..9fc985a0545 100644
--- a/tests/console/commands/test_build.py
+++ b/tests/console/commands/test_build.py
@@ -206,7 +206,7 @@ def test_build_relative_directory_src_layout(
# initializes Poetry before passing the directory.
app = Application()
tester = ApplicationTester(app)
- tester.execute("build --directory .")
+ tester.execute("build --project .")
build_dir = tmp_project_path / "dist"
diff --git a/tests/console/commands/test_version.py b/tests/console/commands/test_version.py
index 5741cf3d9f5..4df35505c99 100644
--- a/tests/console/commands/test_version.py
+++ b/tests/console/commands/test_version.py
@@ -1,14 +1,22 @@
from __future__ import annotations
+import os
+import textwrap
+
+from pathlib import Path
from typing import TYPE_CHECKING
import pytest
+from cleo.testers.application_tester import ApplicationTester
+
+from poetry.console.application import Application
from poetry.console.commands.version import VersionCommand
if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester
+ from pytest_mock import MockerFixture
from poetry.poetry import Poetry
from tests.types import CommandTesterFactory
@@ -132,3 +140,103 @@ def test_dry_run(tester: CommandTester) -> None:
new_pyproject = tester.command.poetry.file.path.read_text(encoding="utf-8")
assert tester.io.fetch_output() == "Bumping version from 1.2.3 to 2.0.0\n"
assert old_pyproject == new_pyproject
+
+
+def test_version_with_project_parameter(
+ fixture_dir: FixtureDirGetter, mocker: MockerFixture
+) -> None:
+ app = Application()
+ tester = ApplicationTester(app)
+
+ orig_version_command = VersionCommand.handle
+
+ def mock_handle(command: VersionCommand) -> int:
+ exit_code = orig_version_command(command)
+
+ command.io.write_line(f"ProjectPath: {command.poetry.pyproject_path.parent}")
+ command.io.write_line(f"WorkingDirectory: {os.getcwd()}")
+
+ return exit_code
+
+ mocker.patch("poetry.console.commands.version.VersionCommand.handle", mock_handle)
+
+ source_dir = fixture_dir("scripts")
+ tester.execute(f"--project {source_dir} version")
+
+ output = tester.io.fetch_output()
+ expected = textwrap.dedent(f"""\
+ scripts 0.1.0
+ ProjectPath: {source_dir}
+ WorkingDirectory: {os.getcwd()}
+ """)
+
+ assert source_dir != Path(os.getcwd())
+ assert output == expected
+
+
+def test_version_with_directory_parameter(
+ fixture_dir: FixtureDirGetter, mocker: MockerFixture
+) -> None:
+ app = Application()
+ tester = ApplicationTester(app)
+
+ orig_version_command = VersionCommand.handle
+
+ def mock_handle(command: VersionCommand) -> int:
+ exit_code = orig_version_command(command)
+
+ command.io.write_line(f"ProjectPath: {command.poetry.pyproject_path.parent}")
+ command.io.write_line(f"WorkingDirectory: {os.getcwd()}")
+
+ return exit_code
+
+ mocker.patch("poetry.console.commands.version.VersionCommand.handle", mock_handle)
+
+ source_dir = fixture_dir("scripts")
+ tester.execute(f"--directory {source_dir} version")
+
+ output = tester.io.fetch_output()
+ expected = textwrap.dedent(f"""\
+ scripts 0.1.0
+ ProjectPath: {source_dir}
+ WorkingDirectory: {source_dir}
+ """)
+
+ assert source_dir != Path(os.getcwd())
+ assert output == expected
+
+
+def test_version_with_directory_and_project_parameter(
+ fixture_dir: FixtureDirGetter, mocker: MockerFixture
+) -> None:
+ app = Application()
+ tester = ApplicationTester(app)
+
+ orig_version_command = VersionCommand.handle
+
+ def mock_handle(command: VersionCommand) -> int:
+ exit_code = orig_version_command(command)
+
+ command.io.write_line(f"ProjectPath: {command.poetry.pyproject_path.parent}")
+ command.io.write_line(f"WorkingDirectory: {os.getcwd()}")
+
+ return exit_code
+
+ mocker.patch("poetry.console.commands.version.VersionCommand.handle", mock_handle)
+
+ source_dir = fixture_dir("scripts")
+ working_directory = source_dir.parent
+ project_path = "./scripts"
+
+ tester.execute(f"--directory {working_directory} --project {project_path} version")
+
+ output = tester.io.fetch_output()
+
+ expected = textwrap.dedent(f"""\
+ scripts 0.1.0
+ ProjectPath: {source_dir}
+ WorkingDirectory: {working_directory}
+ """)
+
+ assert source_dir != working_directory
+ assert output == expected