diff --git a/docs/cli.md b/docs/cli.md index e494902bfec..18e09ff5bf7 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -29,6 +29,7 @@ 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). 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. diff --git a/src/poetry/console/application.py b/src/poetry/console/application.py index d4c77fd2d3d..adbdeb72247 100644 --- a/src/poetry/console/application.py +++ b/src/poetry/console/application.py @@ -1,6 +1,8 @@ from __future__ import annotations +import contextlib import logging +import os import re from contextlib import suppress @@ -25,6 +27,7 @@ if TYPE_CHECKING: from collections.abc import Callable + from collections.abc import Iterator from cleo.events.event import Event from cleo.io.inputs.argv_input import ArgvInput @@ -48,6 +51,17 @@ def _load() -> Command: return _load +@contextlib.contextmanager +def switch_working_directory(path: Path) -> Iterator[Path]: + original_cwd = Path.cwd() + os.chdir(path) + + try: + yield path + finally: + os.chdir(original_cwd) + + COMMANDS = [ "about", "add", @@ -111,6 +125,63 @@ 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"): + 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 @@ -119,7 +190,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, @@ -172,7 +243,9 @@ def _run(self, io: IO) -> int: self._load_plugins(io) - exit_code: int = super()._run(io) + with switch_working_directory(self._working_directory): + exit_code: int = super()._run(io) + return exit_code def _configure_io(self, io: IO) -> None: @@ -335,49 +408,13 @@ def _load_plugins(self, io: IO | None = None) -> 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( - "--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." - ), - ) - ) - - return definition - - @cached_property - def _directory(self) -> Path: - if self._io and self._io.input.option("project"): - return Path(self._io.input.option("project")).absolute() - return Path.cwd() - def main() -> int: exit_code: int = Application().run()