diff --git a/docs/cli.md b/docs/cli.md index b5913b69040..83c4775019e 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -987,6 +987,11 @@ poetry self remove poetry-plugin-export * `--dry-run`: Outputs the operations but will not execute anything (implicitly enables --verbose). +### self init + +The `self init` command creates a local `.poetry` configuration directory for poetry. Poetry will +use configuration and it's dependencies from this directory instead of the system default. + ### self install The `self install` command ensures all additional packages specified are installed in the current diff --git a/docs/configuration.md b/docs/configuration.md index d9c074262c3..5b05d2cb238 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -125,6 +125,8 @@ Poetry uses the following default directories: - Windows: `%APPDATA%\pypoetry` - MacOS: `~/Library/Application Support/pypoetry` +If there is a `.poetry` directory in the project directory, this will override the default configuration directory. + You can override the Config directory by setting the `POETRY_CONFIG_DIR` environment variable. ### Data Directory diff --git a/src/poetry/console/application.py b/src/poetry/console/application.py index 8d325efde1f..b34f91f66ba 100644 --- a/src/poetry/console/application.py +++ b/src/poetry/console/application.py @@ -80,6 +80,7 @@ def _load() -> Command: "env use", # Self commands "self add", + "self init", "self install", "self lock", "self remove", diff --git a/src/poetry/console/commands/self/init.py b/src/poetry/console/commands/self/init.py new file mode 100644 index 00000000000..3df27695b1e --- /dev/null +++ b/src/poetry/console/commands/self/init.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +from pathlib import Path + +from cleo.helpers import option + +from poetry.console.commands.self.self_command import SelfCommand + + +class SelfInitCommand(SelfCommand): + name = "self init" + description = """\ +Initializes a local .poetry directory to be used instead of the global poetry \ +configuration.\ +""" + options = [ + option( + "project-dir", + None, + ( + "The directory containing a pyproject.toml file to which the .poetry " + " directory will be added." + ), + flag=False, + default=None, + ), + ] + help = """\ +The self init command creates and initializes a .poetry directory that\ +contains poetry configuration specific for the project directory instead of using the\ +global poetry configuration. +""" + + loggers = ["poetry.repositories.pypi_repository", "poetry.inspection.info"] + + def __init__(self): + self._system_pyproject = super().system_pyproject + super().__init__() + + def handle(self) -> int: + project_dir = self.option("project-dir") + if project_dir is None: + project_dir = Path.cwd() + self._system_pyproject = Path(project_dir) / ".poetry" / "pyproject.toml" + if self.system_pyproject.exists(): + self.line(f"Poetry settings already exist for project {project_dir}") + self.line_error("\nNo changes were applied.") + return 1 + + self.line(f"Initialising poetry settings for project {project_dir}") + self.system_pyproject.parent.mkdir(parents=True, exist_ok=True) + self.generate_system_pyproject() + return 0 + + @property + def system_pyproject(self) -> Path: + return self._system_pyproject diff --git a/src/poetry/locations.py b/src/poetry/locations.py index 2d3b25da722..7657a6a74a7 100644 --- a/src/poetry/locations.py +++ b/src/poetry/locations.py @@ -17,8 +17,9 @@ DEFAULT_CACHE_DIR = user_cache_path(_APP_NAME, appauthor=False) CONFIG_DIR = Path( - os.getenv("POETRY_CONFIG_DIR") - or user_config_path(_APP_NAME, appauthor=False, roaming=True) + os.getenv("POETRY_CONFIG_DIR") or Path.cwd() / ".poetry" + if (Path.cwd() / ".poetry").exists() + else None or user_config_path(_APP_NAME, appauthor=False, roaming=True) ) # platformdirs 2.0.0 corrected the OSX/macOS config directory from diff --git a/tests/console/commands/self/test_init_plugins.py b/tests/console/commands/self/test_init_plugins.py new file mode 100644 index 00000000000..8a711847744 --- /dev/null +++ b/tests/console/commands/self/test_init_plugins.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import os + +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +from poetry.core.utils.helpers import temporary_directory + + +if TYPE_CHECKING: + from cleo.testers.command_tester import CommandTester + + from tests.types import CommandTesterFactory + + +@pytest.fixture() +def tester(command_tester_factory: CommandTesterFactory) -> CommandTester: + return command_tester_factory("self init") + + +def test_init_no_args( + tester: CommandTester, +) -> None: + with temporary_directory() as tmp_dir: + current_dir = os.getcwd() + try: + os.chdir(tmp_dir) + tester.execute() + assert tester.io.fetch_output() == f"""\ +Initialising poetry settings for project {tmp_dir} +""" + assert (Path(tmp_dir) / ".poetry" / "pyproject.toml").exists() + assert tester.status_code == 0 + finally: + os.chdir(current_dir) + + +def test_init_project_dir( + tester: CommandTester, +) -> None: + with temporary_directory() as tmp_dir: + tester.execute(args=f"--project-dir {tmp_dir}") + assert tester.io.fetch_output() == f"""\ +Initialising poetry settings for project {tmp_dir} +""" + assert (Path(tmp_dir) / ".poetry" / "pyproject.toml").exists() + assert tester.status_code == 0 + + +def test_init_project_dir_already_exists( + tester: CommandTester, +) -> None: + with temporary_directory() as tmp_dir: + pyproject_file = Path(tmp_dir) / ".poetry" / "pyproject.toml" + pyproject_file.parent.mkdir() + pyproject_file.write_text("hello world") + tester.execute(args=f"--project-dir {tmp_dir}") + assert tester.io.fetch_output() == f"""\ +Poetry settings already exist for project {tmp_dir} +""" + assert tester.status_code == 1