Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#8083): Added support for poetry config being stored in a projec… #8084

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/poetry/console/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def _load() -> Command:
"env use",
# Self commands
"self add",
"self init",
"self install",
"self lock",
"self remove",
Expand Down
57 changes: 57 additions & 0 deletions src/poetry/console/commands/self/init.py
Original file line number Diff line number Diff line change
@@ -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 <c1>self init</c1> 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) -> None:
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.reset_poetry()
return 0

@property
def system_pyproject(self) -> Path:
return self._system_pyproject
1 change: 1 addition & 0 deletions src/poetry/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
DEFAULT_CACHE_DIR = user_cache_path(_APP_NAME, appauthor=False)
CONFIG_DIR = Path(
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)
)

Expand Down
55 changes: 55 additions & 0 deletions tests/console/commands/self/test_add_plugins.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
from typing import cast

from poetry.core.utils.helpers import temporary_directory

import poetry.locations

from poetry.console.commands.self.add import SelfAddCommand


if TYPE_CHECKING:
Expand Down Expand Up @@ -29,6 +37,11 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("self add")


@pytest.fixture()
def init_command_tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("self init")


def assert_plugin_add_result(
tester: CommandTester,
expected: str,
Expand Down Expand Up @@ -64,6 +77,48 @@ def test_add_no_constraint(
assert_plugin_add_result(tester, expected, "^0.1.0")


def test_add_after_init(
tester: CommandTester,
init_command_tester: CommandTester,
repo: TestRepository,
) -> None:
with temporary_directory() as tmp_dir:
repo.add_package(Package("poetry-plugin", "0.1.0"))
init_command_tester.execute(args=f"--project-dir={tmp_dir}")
current_config_dir = poetry.locations.CONFIG_DIR
try:
poetry.locations.CONFIG_DIR = Path(tmp_dir) / ".poetry"
cast(SelfAddCommand, tester._command).reset_poetry()
tester.execute("poetry-plugin")

expected_add_output = """\
Using version ^0.1.0 for poetry-plugin

Updating dependencies
Resolving dependencies...

Package operations: 1 install, 0 updates, 0 removals

• Installing poetry-plugin (0.1.0)

Writing lock file
"""

expected_in_pyproject_toml = """\
[tool.poetry.group.additional.dependencies]
poetry-plugin = "^0.1.0"
"""

assert_plugin_add_result(tester, expected_add_output, "^0.1.0")
assert (
expected_in_pyproject_toml
in (Path(tmp_dir) / ".poetry" / "pyproject.toml").read_text()
)

finally:
poetry.locations.CONFIG_DIR = current_config_dir


def test_add_with_constraint(
tester: CommandTester,
repo: TestRepository,
Expand Down
64 changes: 64 additions & 0 deletions tests/console/commands/self/test_init.py
Original file line number Diff line number Diff line change
@@ -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