|
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
|
|
15 | 13 | from cleo.events.event_dispatcher import EventDispatcher
|
16 | 14 | from cleo.exceptions import CleoError
|
17 | 15 | from cleo.formatters.style import Style
|
| 16 | +from cleo.io.inputs.argv_input import ArgvInput |
18 | 17 |
|
19 | 18 | from poetry.__version__ import __version__
|
20 | 19 | from poetry.console.command_loader import CommandLoader
|
|
28 | 27 | from typing import Any
|
29 | 28 |
|
30 | 29 | from cleo.events.event import Event
|
31 |
| - from cleo.io.inputs.argv_input import ArgvInput |
32 | 30 | from cleo.io.inputs.definition import Definition
|
33 | 31 | from cleo.io.inputs.input import Input
|
34 | 32 | from cleo.io.io import IO
|
@@ -277,40 +275,78 @@ def _configure_custom_application_options(self, io: IO) -> None:
|
277 | 275 | is_directory=True,
|
278 | 276 | )
|
279 | 277 |
|
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) |
| 278 | + def _configure_run_command(self, io: IO) -> None: |
| 279 | + """ |
| 280 | + Configures the input for the "run" command to properly handle cases where the user |
| 281 | + executes commands such as "poetry run -- <subcommand>". This involves reorganizing |
| 282 | + input tokens to ensure correct parsing and execution of the run command. |
| 283 | + """ |
| 284 | + command_name = io.input.first_argument |
| 285 | + if command_name == "run": |
| 286 | + original_input = cast(ArgvInput, io.input) |
| 287 | + tokens: list[str] = original_input._tokens |
| 288 | + |
| 289 | + if "--" in tokens: |
| 290 | + # this means the user has done the right thing and used "poetry run -- echo hello" |
| 291 | + # in this case there is not much we need to do, we can skip the rest |
| 292 | + return |
| 293 | + |
| 294 | + # find the correct command index, in some cases this might not be first occurrence |
| 295 | + # eg: poetry -C run run echo |
| 296 | + command_index = tokens.index(command_name) |
| 297 | + |
| 298 | + while command_index < (len(tokens) - 1): |
| 299 | + try: |
| 300 | + # try parsing the tokens so far |
| 301 | + _ = ArgvInput( |
| 302 | + [self._name or "", *tokens[: command_index + 1]], |
| 303 | + definition=self.definition, |
| 304 | + ) |
| 305 | + break |
| 306 | + except CleoError: |
| 307 | + # parsing failed, try finding the next "run" token |
| 308 | + command_index += tokens[command_index + 1 :].index(command_name) + 1 |
| 309 | + else: |
| 310 | + # looks like we reached the end of the road, let clea deal with it |
| 311 | + return |
| 312 | + |
| 313 | + # fetch tokens after the "run" command |
| 314 | + tokens_without_command = tokens[command_index + 1 :] |
| 315 | + |
| 316 | + # we create a new input for parsing the subcommand pretending |
| 317 | + # it is poetry command |
| 318 | + without_command = ArgvInput( |
| 319 | + [self._name or "", *tokens_without_command], None |
| 320 | + ) |
| 321 | + |
| 322 | + # the first argument here is the subcommand |
| 323 | + subcommand = without_command.first_argument |
| 324 | + subcommand_index = ( |
| 325 | + tokens.index(subcommand) if subcommand else command_index + 1 |
| 326 | + ) |
311 | 327 |
|
| 328 | + # recreate the original input reordering in the following order |
| 329 | + # - all tokens before "run" command |
| 330 | + # - all tokens after "run" command but before the subcommand |
| 331 | + # - the "run" command token |
| 332 | + # - the "--" token to normalise the form |
| 333 | + # - all remaining tokens starting with the subcommand |
| 334 | + run_input = ArgvInput( |
| 335 | + [ |
| 336 | + self._name or "", |
| 337 | + *tokens[:command_index], |
| 338 | + *tokens[command_index + 1 : subcommand_index], |
| 339 | + command_name, |
| 340 | + "--", |
| 341 | + *tokens[subcommand_index:], |
| 342 | + ] |
| 343 | + ) |
| 344 | + |
| 345 | + # reset the input to our constructed form |
312 | 346 | io.set_input(run_input)
|
313 | 347 |
|
| 348 | + def _configure_io(self, io: IO) -> None: |
| 349 | + self._configure_run_command(io) |
314 | 350 | super()._configure_io(io)
|
315 | 351 |
|
316 | 352 | def register_command_loggers(
|
|
0 commit comments