|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | import logging |
4 | | -import re |
5 | 4 |
|
6 | | -from contextlib import suppress |
7 | 5 | from importlib import import_module |
8 | 6 | from pathlib import Path |
9 | 7 | from typing import TYPE_CHECKING |
|
13 | 11 | from cleo.events.console_command_event import ConsoleCommandEvent |
14 | 12 | from cleo.events.console_events import COMMAND |
15 | 13 | from cleo.events.event_dispatcher import EventDispatcher |
16 | | -from cleo.exceptions import CleoError |
17 | 14 | from cleo.formatters.style import Style |
| 15 | +from cleo.io.inputs.argv_input import ArgvInput |
18 | 16 |
|
19 | 17 | from poetry.__version__ import __version__ |
20 | 18 | from poetry.console.command_loader import CommandLoader |
|
28 | 26 | from typing import Any |
29 | 27 |
|
30 | 28 | from cleo.events.event import Event |
31 | | - from cleo.io.inputs.argv_input import ArgvInput |
32 | 29 | from cleo.io.inputs.definition import Definition |
33 | 30 | from cleo.io.inputs.input import Input |
34 | 31 | from cleo.io.io import IO |
@@ -277,40 +274,61 @@ def _configure_custom_application_options(self, io: IO) -> None: |
277 | 274 | is_directory=True, |
278 | 275 | ) |
279 | 276 |
|
280 | | - def _configure_io(self, io: IO) -> None: |
281 | | - # We need to check if the command being run |
282 | | - # is the "run" command. |
283 | | - definition = self.definition |
284 | | - with suppress(CleoError): |
285 | | - io.input.bind(definition) |
286 | | - |
287 | | - name = io.input.first_argument |
288 | | - if name == "run": |
289 | | - from poetry.console.io.inputs.run_argv_input import RunArgvInput |
290 | | - |
291 | | - input = cast("ArgvInput", io.input) |
292 | | - run_input = RunArgvInput([self._name or "", *input._tokens]) |
293 | | - # For the run command reset the definition |
294 | | - # with only the set options (i.e. the options given before the command) |
295 | | - for option_name, value in input.options.items(): |
296 | | - if value: |
297 | | - option = definition.option(option_name) |
298 | | - run_input.add_parameter_option("--" + option.name) |
299 | | - if option.shortcut: |
300 | | - shortcuts = re.split(r"\|-?", option.shortcut.lstrip("-")) |
301 | | - shortcuts = [s for s in shortcuts if s] |
302 | | - for shortcut in shortcuts: |
303 | | - run_input.add_parameter_option("-" + shortcut.lstrip("-")) |
304 | | - |
305 | | - with suppress(CleoError): |
306 | | - run_input.bind(definition) |
307 | | - |
308 | | - for option_name, value in input.options.items(): |
309 | | - if value: |
310 | | - run_input.set_option(option_name, value) |
| 277 | + def _configure_run_command(self, io: IO) -> None: |
| 278 | + """ |
| 279 | + Configures the input for the "run" command to properly handle cases where the user |
| 280 | + executes commands such as "poetry run -- <subcommand>". This involves reorganizing |
| 281 | + input tokens to ensure correct parsing and execution of the run command. |
| 282 | + """ |
| 283 | + command_name = io.input.first_argument |
| 284 | + if command_name == "run": |
| 285 | + original_input = cast(ArgvInput, io.input) |
| 286 | + tokens: list[str] = original_input._tokens |
| 287 | + |
| 288 | + if "--" in tokens: |
| 289 | + # this means the user has done the right thing and used "poetry run -- echo hello" |
| 290 | + # in this case there is not much we need to do, we can skip the rest |
| 291 | + return |
| 292 | + |
| 293 | + command_index = tokens.index(command_name) |
| 294 | + |
| 295 | + # fetch tokens after the "run" command |
| 296 | + tokens_without_command = tokens[command_index + 1 :] |
| 297 | + |
| 298 | + # we create a new input for parsing the subcommand pretending |
| 299 | + # it is poetry command |
| 300 | + without_command = ArgvInput( |
| 301 | + [self._name or "", *tokens_without_command], None |
| 302 | + ) |
| 303 | + |
| 304 | + # the first argument here is the subcommand |
| 305 | + subcommand = without_command.first_argument |
| 306 | + subcommand_index = ( |
| 307 | + tokens.index(subcommand) if subcommand else command_index + 1 |
| 308 | + ) |
311 | 309 |
|
| 310 | + # recreate the original input reordering in the following order |
| 311 | + # - all tokens before "run" command |
| 312 | + # - all tokens after "run" command but before the subcommand |
| 313 | + # - the "run" command token |
| 314 | + # - the "--" token to normalise the form |
| 315 | + # - all remaining tokens starting with the subcommand |
| 316 | + run_input = ArgvInput( |
| 317 | + [ |
| 318 | + self._name or "", |
| 319 | + *tokens[:command_index], |
| 320 | + *tokens[command_index + 1 : subcommand_index], |
| 321 | + command_name, |
| 322 | + "--", |
| 323 | + *tokens[subcommand_index:], |
| 324 | + ] |
| 325 | + ) |
| 326 | + |
| 327 | + # reset the input to our constructed form |
312 | 328 | io.set_input(run_input) |
313 | 329 |
|
| 330 | + def _configure_io(self, io: IO) -> None: |
| 331 | + self._configure_run_command(io) |
314 | 332 | super()._configure_io(io) |
315 | 333 |
|
316 | 334 | def register_command_loggers( |
|
0 commit comments