poetry icon indicating copy to clipboard operation
poetry copied to clipboard

Document difference between poetry plugins and entrypoints.

Open freshstrangemusic opened this issue 7 years ago • 12 comments

  • [x] I have searched the issues of this repo and believe that this is not a duplicate.

Issue

From a cursory glance, it looks like plugins are converted to entrypoints, but it would be good to clarify this in the documentation because it reads as if they are different:

Poetry supports arbitrary plugins which work similarly to setuptools entry points. (2)

freshstrangemusic avatar Nov 21 '18 03:11 freshstrangemusic

@sdispater - I don't want to open a new issue, so I'll comment here. I'm a little confused, because https://github.com/sdispater/poetry/issues/693 would seem to be the right place but that issue has become muddied with discussions around cleo, python version specification, etc.

This issue asks a specific question, but mine is a little broader: It appears that poetry has documentation for its own plugins along with a way of specifying plugins for your own projects. However, it doesn't have any examples/documentation about how to use the latter.

As a concrete example, how would I replace this specification and use of entrypoints if I moved that project to poetry?

cjw296 avatar Feb 19 '19 07:02 cjw296

I'm not sure why poetry specific plugins are documented since they don't seem to be implemented yet although I might be wrong since I just did a simple grep search.

As @brennie said plugins are simply a way to specify entry points so you wouldn't need to change anything to use them. I'd imagine you'd specify them similarly:

[tool.poetry.scripts]
archivist = "archivist.main:main"

[tool.poetry.plugins."archivist.repo"]
git = "archivist.main:main"

[tool.poetry.plugins."archivist.source"]
crontab = "archivist.sources.crontab:Plugin"
jenkins = "archivist.sources.jenkins:Plugin"
packages = "archivist.sources.packages:Plugin"
paths = "archivist.sources.paths:Plugin"

[tool.poetry.plugins."archivist.notification"]
email = "archivist.notifications.email:Plugin"
stream = "archivist.notifications.stream:Plugin"

orlnub123 avatar Feb 27 '19 17:02 orlnub123

@orlnub123 - great, so this is just a documentation issue. Related question: Should I specify any dependency to make sure the pkg_resources package is available? I seem to remember that comes from setuptools, so curious about whether that'll "always" be available across virtualenvs, conda environments, etc?

cjw296 avatar Mar 01 '19 06:03 cjw296

@cjw296 It's probably possible to have an environment where setuptools isn't available at runtime so I'd say it's a good idea to declare it as a dependency.

orlnub123 avatar Mar 01 '19 15:03 orlnub123

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 13 '19 16:11 stale[bot]

not stale

cjw296 avatar Nov 15 '19 03:11 cjw296

I still do not get it. What is the difference between tool.poetry.script and the stuff you declare beneath tool.poetry.plugins? Scripts I may invoke with "poetry run SCRIPT_NAME".

mfriedenhagen avatar Dec 05 '20 10:12 mfriedenhagen

@mfriedenhagen

As far as I understood it is a taken out of the entry points feature from setuptools:

  • https://setuptools.readthedocs.io/en/latest/userguide/entry_point.html

Basically you have named entry points in different categories. The most common category is console_scripts. If you add an entry point in that category, then an executable of the same name is created. The executable will be created so that it runs the function specified as value for the entry point.

In setuptools setup.cfg you might have:

[options.entry_points]
console_scripts =
    mycommand = mypackage.mymodule:my_function

This should result in a mycommand executable that when called, runs the function my_function from mypackage.mymodule. So more or less:

#!/usr/bin/env python

if __name__ == '__main__':
    import mypackage
    mypackage.mymodule.my_function()

In poetry you would write in pyproject.toml either one of the following two:

[tool.poetry.scripts]
mycommand = "mypackage.mymodule:my_function"
[tool.poetry.plugins."console_scripts"]
"mycommand" = "mypackage.mymodule:my_function"

As far as I know they should be the same. Except maybe that first notation also allows to do poetry run mycommand (I am not sure).


As for the more general "plug-ins" side of this, I find it a bit hard to explain...

First I am a bit conflicted on the divide that was created in poetry between scripts and plugins. In both cases these are entry points, true they might be used for console scripts or plug-ins, but maybe for other things as well.

Basically you could write a pluggable application, that will look for any entry point registered in the specific category of your application (you define the name yourself, and it could look like awesomeapplication_plugin_category for example), and then your application can decide to call the associated functions to enhance the features of your pluggable application with plug-ins. So once your pluggable application is published and documented, I could write a plug-in for it:

[tool.poetry.plugins."awesomeapplication_plugin_category"]
"myawesomeplugin_entry_point" = "myawesomeplugin.mymodule:my_function"

So console_scripts is just one category of entry points and [tool.poetry.scripts] is in a way some kind of a shortcut for [tool.poetry.plugins."console_scripts"].

sinoroc avatar Dec 05 '20 11:12 sinoroc

Thanks a lot @sinoroc. That now makes sense somehow. So poetry currently does not allow to extend itself with plugins but provides a means to build a package which enhances other software like e.g. pytest, so I the following sample of https://docs.pytest.org/en/stable/writing_plugins.html#setuptools-entry-points could probably be written as

[tool.poetry.plugins."pytest11"]
name_of_plugin = "myproject.pluginmodule"

mfriedenhagen avatar Dec 05 '20 20:12 mfriedenhagen

poetry currently does not allow to extend itself with plugins but provides a means to build a package which enhances other software like e.g. pytest

Exactly.

the following sample of https://docs.pytest.org/en/stable/writing_plugins.html#setuptools-entry-points could probably be written as

[tool.poetry.plugins."pytest11"]
name_of_plugin = "myproject.pluginmodule"

Yes, seems right.

Poetry's own plug-in system is scheduled for v1.2. The way this future plug-in system is drafted now, if you were to write a plug-in for poetry in the future, it might look like this in your pyroject.toml:

[tool.poetry.plugins."poetry.plugin"]
demo = "poetry_demo_plugin.plugin:MyPlugin"

-- https://github.com/python-poetry/poetry/blame/605d786a942301374309d67c09d6579bf9165d75/docs/docs/plugins.md#L31-L32

sinoroc avatar Dec 06 '20 10:12 sinoroc

FWIW, I've played around with this a tiny bit and found https://github.com/python-poetry/poetry/issues/658#issuecomment-467952140 to be correct. Here's a very simple example project for using poetry plugins in a CLI app: https://github.com/sdruskat/poetry-entrypoints.

sdruskat avatar Mar 24 '22 10:03 sdruskat

I was trying to have a plugin that would be automatically loaded together with my main application.

For context, I am using the Registry design pattern and am using decorators to add new classes to the register. Meaning the module would need to be imported for the classes to be registered. So basically broadcasting/advertising functionality.

For those that get here trying to do something similar, this documentation is what helped me: Advertising behavior.

I basically had to register the python module as a plugin, i.e.:

[tool.poetry.plugins."main_app.plugins"]
"plugin_name" = "path.to.plugin"

And then load available plugins in the __init__.py file at the root of the main_app:

from importlib.metadata import entry_points

for entry_point in entry_points(group="main_app.plugins"):
    # You can add more plugin names here
    if entry_point.name in ["plugin_name"]:
        entry_point.load()

leoschet avatar Feb 21 '24 12:02 leoschet