python-poetry / python-poetry/poetry

Poetry can get locked into an unusable state with plugins: 'The command "..." already exists'

Open
#6,456 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area/plugin-api kind/bug status/confirmed version/1.2.0
Dominant language
Python
Stars
34.3k
Forks
2.5k
Avg merge
2d 19h
Merged PRs (30d)
30

Description

  • I am on the latest Poetry version.
  • I have searched the issues of this repo and believe that this is not a duplicate.
  • If an exception occurs when executing a command, I executed it again in debug mode (-vvv option).
  • OS version and name: Windows 11
  • Poetry version: 1.2.0
  • Link of a Gist with the contents of your pyproject.toml file: N/A

Issue

If plugins with conflicting commands are installed, Poetry gets locked into an unusable state where even --help fails, and there's no way to recover unless you manually activate Poetry's internal venv and remove the plugins. Someone reported this to me because I had a plugin called poetry-dynamic-versioning-plugin that I've since deprecated and merged into poetry-dynamic-versioning[plugin]. If you still have the deprecated one installed and then install the newly updated one, it breaks Poetry.

$ poetry self add poetry-dynamic-versioning-plugin
Using version ^0.4.0 for poetry-dynamic-versioning-plugin

Updating dependencies
Resolving dependencies... (0.5s)

Writing lock file

Package operations: 4 installs, 0 updates, 0 removals

  • Installing markupsafe (2.1.1)
  • Installing dunamai (1.13.0)
  • Installing jinja2 (3.1.2)
  • Installing poetry-dynamic-versioning-plugin (0.4.0)

$ poetry self add poetry-dynamic-versioning[plugin]
Using version ^0.18.0 for poetry-dynamic-versioning

Updating dependencies
Resolving dependencies... (0.2s)

Writing lock file

Package operations: 1 install, 0 updates, 0 removals

  • Installing poetry-dynamic-versioning (0.18.0)

$ poetry --help

The command "dynamic-versioning" already exists.

With debug mode:

$ poetry --help -vvv

  Stack trace:

  5  ~\.local\pipx\venvs\poetry\lib\site-packages\cleo\application.py:329 in run
      327│
      328│             try:
    → 329│                 exit_code = self._run(io)
      330│             except Exception as e:
      331│                 if not self._catch_exceptions:

  4  ~\.local\pipx\venvs\poetry\lib\site-packages\poetry\console\application.py:183 in _run
      181│         self._disable_cache = io.input.has_parameter_option("--no-cache")
      182│
    → 183│         self._load_plugins(io)
      184│
      185│         exit_code: int = super()._run(io)

  3  ~\.local\pipx\venvs\poetry\lib\site-packages\poetry\console\application.py:349 in _load_plugins
      347│             manager = PluginManager(ApplicationPlugin.group)
      348│             manager.load_plugins()
    → 349│             manager.activate(self)
      350│
      351│         self._plugins_loaded = True

  2  ~\.local\pipx\venvs\poetry\lib\site-packages\poetry\plugins\plugin_manager.py:71 in activate
       69│     def activate(self, *args: Any, **kwargs: Any) -> None:
       70│         for plugin in self._plugins:
    →  71│             plugin.activate(*args, **kwargs)
       72│
       73│     def _load_plugin_entry_point(self, ep: metadata.EntryPoint) -> None:

  1  ~\.local\pipx\venvs\poetry\lib\site-packages\poetry_dynamic_versioning\plugin.py:93 in activate
       91│         self._application = application
       92│
    →  93│         application.command_loader.register_factory(
       94│             "dynamic-versioning", lambda: DynamicVersioningCommand(application)
       95│         )

  LogicException

  The command "dynamic-versioning" already exists.

  at ~\.local\pipx\venvs\poetry\lib\site-packages\poetry\console\command_loader.py:20 in register_factory
       16│     def register_factory(
       17│         self, command_name: str, factory: Callable[[], Command]
       18│     ) -> None:
       19│         if command_name in self._factories:
    →  20│             raise LogicException(f'The command "{command_name}" already exists.')
       21│
       22│         self._factories[command_name] = factory
       23│

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the conflicting-plugin case with the two poetry self add commands, then read poetry/console/application.py, poetry/plugins/plugin_manager.py, and cleo's command_loader.py at the stack-trace entry points. Done means Poetry can recover from conflicting plugin commands instead of making even --help fail.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.