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

fix: issues with argument parsing in poetry 2.0 plugin #277

Merged
merged 1 commit into from
Feb 24, 2025
Merged
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
11 changes: 8 additions & 3 deletions poethepoet/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,14 @@ def load(self, target_path: Optional[Union[Path, str]] = None, strict: bool = Tr
break

else:
raise PoeException(
f"No poe configuration found from location {target_path}"
)
if target_path is not None:
raise PoeException(
f"No poe configuration found from location {target_path}"
)
else:
raise PoeException(
f"No poe configuration found from location {self._project_dir}"
)

self._load_includes(strict=strict)

Expand Down
4 changes: 2 additions & 2 deletions poethepoet/executor/poetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def execute(

# Run this task with `poetry run`
return self._execute_cmd(
(self._poetry_cmd(), "run", *cmd),
(self._poetry_cmd(), "--no-plugins", "run", *cmd),
input=input,
use_exec=use_exec,
)
Expand Down Expand Up @@ -88,7 +88,7 @@ def _get_poetry_virtualenv(self, force: bool = True):

exec_cache["poetry_virtualenv"] = (
Popen(
(self._poetry_cmd(), "env", "info", "-p"),
(self._poetry_cmd(), "--no-plugins", "env", "info", "-p"),
stdout=PIPE,
cwd=self.context.config.project_dir,
env=clean_env,
Expand Down
84 changes: 50 additions & 34 deletions poethepoet/plugin.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
from pathlib import Path
from __future__ import annotations

from typing import TYPE_CHECKING, Any

from cleo.commands.command import Command
from cleo.events.console_command_event import ConsoleCommandEvent
from cleo.events.console_events import COMMAND, TERMINATE
from cleo.events.event_dispatcher import EventDispatcher
from cleo.io.io import IO
from poetry.console.application import COMMANDS, Application
from poetry.plugins.application_plugin import ApplicationPlugin

from .exceptions import PoePluginException

if TYPE_CHECKING:
from cleo.events.console_command_event import ConsoleCommandEvent
from cleo.events.event_dispatcher import EventDispatcher
from cleo.io.io import IO

from .config import PoeConfig


class PoeCommand(Command):
prefix: str
command_prefix: str = "poe"
poe_config: PoeConfig

def __init__(self):
super().__init__()
Expand Down Expand Up @@ -45,9 +48,10 @@ def handle(self):
raise SystemExit(task_status)

@classmethod
def get_poe(cls, application: Application, io: IO):
def get_poe(
cls, application: Application, io: IO, poe_config: PoeConfig | None = None
):
from .app import PoeThePoet
from .config import PoeConfig

try:
from poetry.utils.env import EnvManager
Expand All @@ -56,23 +60,17 @@ def get_poe(cls, application: Application, io: IO):
except: # noqa: E722
poetry_env_path = None

# Get the cwd from poetry to ensure '--directory subdir' usage
try:
cwd = application.poetry.pyproject_path
except AttributeError:
cwd = Path().resolve()
config = PoeConfig(cwd=cwd)
poe_config = poe_config or cls.poe_config

if io.output.is_quiet():
config._baseline_verbosity = -1
poe_config._baseline_verbosity = -1
elif io.is_very_verbose():
config._baseline_verbosity = 2
poe_config._baseline_verbosity = 2
elif io.is_verbose():
config._baseline_verbosity = 1
poe_config._baseline_verbosity = 1

return PoeThePoet(
cwd=cwd,
config=config,
config=poe_config,
output=io.output.stream,
poetry_env_path=poetry_env_path,
program_name=f"poetry {cls.command_prefix}",
Expand Down Expand Up @@ -126,10 +124,11 @@ def _activate(self, application: Application) -> None:
f"Poe task {task_name!r} conflicts with a poetry command. "
"Please rename the task or the configure a command prefix."
)
self._register_command(application, task_name, task)
self._register_command(application, poe_config, task_name, task)
else:
self._register_command(
application,
poe_config,
"",
{"help": "Run poe tasks defined for this project"},
command_prefix,
Expand All @@ -138,23 +137,23 @@ def _activate(self, application: Application) -> None:
if task_name.startswith("_"):
continue
self._register_command(
application, task_name, task, f"{command_prefix} "
application, poe_config, task_name, task, f"{command_prefix} "
)

self._monkey_patch_cleo(command_prefix, list(poe_tasks.keys()))

self._register_command_event_handler(
application, poe_config._project_config.get("poetry_hooks", {})
application, poe_config._project_config.get("poetry_hooks", {}), poe_config
)

@classmethod
def _get_config(cls, application: Application) -> "PoeConfig":
def _get_config(cls, application: Application) -> PoeConfig:
from .config import PoeConfig

# Try respect poetry's '--directory' if set
try:
pyproject_dir = application.poetry.pyproject_path.parent
except (AttributeError, RuntimeError):
except AttributeError:
pyproject_dir = None

poe_config = PoeConfig(cwd=pyproject_dir)
Expand All @@ -169,7 +168,12 @@ def _validate_command_prefix(self, command_prefix: str):
)

def _register_command(
self, application: Application, task_name: str, task: Any, prefix: str = ""
self,
application: Application,
poe_config: PoeConfig,
task_name: str,
task: Any,
prefix: str = "",
):
command_name = prefix + task_name
task_help = task.get("help", "") if isinstance(task, dict) else ""
Expand All @@ -178,12 +182,17 @@ def _register_command(
type(
(task_name or prefix).replace("-", "").capitalize() + "Command",
(PoeCommand,),
{"name": command_name, "description": task_help, "prefix": prefix},
{
"name": command_name,
"description": task_help,
"prefix": prefix,
"poe_config": poe_config,
},
),
)

def _register_command_event_handler(
self, application: Application, hooks: dict[str, str]
self, application: Application, hooks: dict[str, str], poe_config: PoeConfig
):
if not hooks:
return
Expand All @@ -201,16 +210,16 @@ def _register_command_event_handler(
if pre_hooks:
application.event_dispatcher.add_listener(
COMMAND,
self._get_command_event_handler(pre_hooks, application),
self._get_command_event_handler(pre_hooks, application, poe_config),
)
if post_hooks:
application.event_dispatcher.add_listener(
TERMINATE,
self._get_command_event_handler(post_hooks, application),
self._get_command_event_handler(post_hooks, application, poe_config),
)

def _get_command_event_handler(
self, hooks: dict[str, str], application: Application
self, hooks: dict[str, str], application: Application, poe_config: PoeConfig
):
def command_event_handler(
event: ConsoleCommandEvent,
Expand All @@ -223,7 +232,7 @@ def command_event_handler(

import shlex

task_status = PoeCommand.get_poe(application, event.io)(
task_status = PoeCommand.get_poe(application, event.io, poe_config)(
cli_args=shlex.split(task), internal=True
)

Expand Down Expand Up @@ -281,10 +290,17 @@ def _run(self, io):

def _index_of_first_non_option(tokens: list[str]):
"""
Find the index of the first token that doesn't start with `-`
Find the index of the first token that doesn't start with `-`, and isn't directly
preceded by either `--project` or `--directory`.

Returns len(tokens) if none is found.
"""
return next(
(index for index, token in enumerate(tokens) if token[0] != "-"),
len(tokens),
)

options_with_args = ("--project", "--directory")
previous_token = ""
for index, token in enumerate(tokens):
if token[0] != "-" and previous_token not in options_with_args:
return index
previous_token = token
else:
return len(tokens)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ sequence = ["docs-check", "style", "types", "lint", "test"]
[tool.poe.tasks.install-poetry-plugin]
help = "Install or update this project as a plugin in poetry"
sequence = [
{ cmd = "poetry self remove poethepoet"},
{ cmd = "poetry --no-plugins self remove poethepoet"},
{ cmd = "poetry self add \"${POE_ROOT}[poetry_plugin]\""}
]
ignore_fail = true
Expand Down
21 changes: 17 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,11 +244,14 @@ def run_poe_main(
return run_poe_main


@pytest.fixture(scope="session")
def run_poetry(use_venv, poe_project_path, version: str = "2.0.0"):
venv_location = poe_project_path / "tests" / "temp" / "poetry_venv"
def run_poetry(use_venv, poe_project_path, version):
venv_location = poe_project_path / "tests" / "temp" / f"poetry_venv_{version}"

def run_poetry(args: list[str], cwd: str, env: Optional[dict[str, str]] = None):
def run_poetry(
args: list[str],
cwd: str,
env: Optional[dict[str, str]] = None,
):
venv = Virtualenv(venv_location)

cmd = (venv.resolve_executable("python"), "-m", "poetry", *args)
Expand Down Expand Up @@ -282,6 +285,16 @@ def run_poetry(args: list[str], cwd: str, env: Optional[dict[str, str]] = None):
yield run_poetry


@pytest.fixture(scope="session")
def run_poetry_1(use_venv, poe_project_path):
yield from run_poetry(use_venv, poe_project_path, version="1.8.2")


@pytest.fixture(scope="session")
def run_poetry_2(use_venv, poe_project_path):
yield from run_poetry(use_venv, poe_project_path, version="2.0.0")


@pytest.fixture(scope="session")
def esc_prefix(is_windows):
"""
Expand Down
Loading
Loading