Python Entry Points Explained

In this post I’m going to explain about entry points in Python. Most people know entry points as the little snippet that you put in your setup.py file to make your package available as a script on the command line, but they can be used for so much more. I’m going to show you how to use entry points as a modular plug-in architecture. This is super useful if you want to let other people write Python packages that interact or add abilities to your existing Python package at runtime.


This is a companion discussion topic for the original entry at http://amir.rachum.com/blog/2017/07/28/python-entry-points/

Dear PSF,

Please hire Amir to rewrite every python tutorial ever.

Regards,
Users Everywhere

Seriously, though, this was fantastic. Also super informative.

Cool post, thanks for the examples and explanation around entry points. Without examples like this, its hard to discover excellent features of python such as this… So keep it coming!

Ah! Cool one :snake: !!

Since you are using docopt maybe you can suggest to put it as a package dependency in setup.py:

install_requires=[
    'docopt',
],

Is it just me, or are those examples really garbled? I see codes all in one line. :frowning:

Muy buen post. pero no puedo ejecutar el ultimo paso:
C:\Python27\scripts>snek --type cute
Traceback (most recent call last):
File “C:\Python27\scripts\snek-script.py”, line 9, in
load_entry_point(‘snek’, ‘console_scripts’, ‘snek’)()
File “c:\python27\scripts\snek\snek.py”, line 42, in main
print(get_sneks()[snek_type])
KeyError: ‘cute’

Hi Amir, the tutorial didn’t work on Mac. From the first snek command:

cchilders:/tmp/snek 
$ python setup.py develop
running develop
running egg_info
writing snek.egg-info/PKG-INFO
writing top-level names to snek.egg-info/top_level.txt
writing dependency_links to snek.egg-info/dependency_links.txt
writing entry points to snek.egg-info/entry_points.txt
reading manifest file 'snek.egg-info/SOURCES.txt'
writing manifest file 'snek.egg-info/SOURCES.txt'
running build_ext
Creating /usr/local/lib/python2.7/site-packages/snek.egg-link (link to .)
snek 0.0.0 is already the active version in easy-install.pth
Installing snek script to /usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/bin

Installed /private/tmp/snek
Processing dependencies for snek==0.0.0
Finished processing dependencies for snek==0.0.0

cchilders:/tmp/snek 
$ snek
-bash: snek: command not found

Disappointing, like 90% of tutorials, it doesn’t work on user’s computer and no way to know how. I can’t finish it now

Great post. To up the game, the attention is drawn to Click.

Enjoyed your humorous context, nice work making the explanation engaging :smiley:

Interesting article, I enjoyed the humor! :slight_smile:

For those interested in plugins and entry points, I suggest to take a look at pluggy, which powers pytest, tox and devpi.

@roman_sarmiento Hola! Necesitas actualizar el codigo con los últimos pasos , ya que hasta ese punto solo regresara el diccionario con las llaves normal y fancy por ello la excepcion KeyError.
Saludos!

@cody_k Hi, I followed this tutorial on mac and didn’t have any issue.
If I can help you let me know.

@cody_k, It looks like the installation succeeds but /usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/bin is not in your PATH.

In your ~/.bashrc (Sometimes called ~/.bash_profile on Mac) add the following line at the end of the file:

export PATH="/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/bin:$PATH"

Then either open a new terminal tab or just run the following command in the current tab:

source ~/.bash_profile

That should fix your problem

One important thing to be aware about when dealing with entry_points is that they are really, really sllloooowwww.

This is because importing pkg_resources is slow.
A console script made with entry_point will take between 200ms - 5 seconds to start.
This is still an open issue in setuptools.

Luckily there’s a workaround explain in the following blog post:
https://coldfix.eu/2017/02/25/slow-entrypoints/

i can’t understand this part.
for entry_point in pkg_resources.iter_entry_points(‘snek_types’):

can you please show us what’s in this pkg_resources or what it is? anything to help understand the code!

pkg_resources is a Python package. Specifically, it provides iter_entry_points which returns an iterator for all the entry points that were registered under the name you specified (in this case, 'snek_types').

1 Like

I thought this was a great little tutorial.

For people having problems, here’s what the final setup.py should look like at the end:

from setuptools import setup
    setup(
        name='cute_snek',
        entry_points={
            'snek_types': [
                'cute = cute_snek:cute_snek',
                'normal = snek:normal_snek',
                'fancy = snek:fancy_snek',
            ],
            'console_scripts': [
                'snek = snek:main',
            ],
        }
    )

The key is how he moved the original snek types (‘normal’ and ‘fancy’) into the entry_points so that the code in snek.py can find them:

def get_sneks():
    sneks = {}
    for entry_point in pkg_resources.iter_entry_points('snek_types'):
        sneks[entry_point.name] = entry_point.load()
    return sneks

Notice how the function get_sneks simply iterates through the snek_types that are listed in the setup.py file.
Notice from the setup.py file that the code expects to find the cute_snek in a file called cute_snek.py (which simply contains the text string: cute_snek = r"…" exactly as he shows it).

And the code expects to find the normal_snek and fancy_snek strings in the original file snek.py since in setup.py it maps ‘normal’ to the normal_spec variable inside snek.py:
‘normal = snek:normal_snek’

Also, to see what the “console_scripts” entry point in setup.py does, it’s instructive to do:
>which snek
and then follow the resulting path to the installed snek script:

#!/some_path_to_your_python_env_here/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'cute-snek','console_scripts','snek'
__requires__ = 'cute-snek'
import re
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(
        load_entry_point('cute-snek', 'console_scripts', 'snek')()
    )

Love this tutorial! Just login to say thank you.

great tutorial, added a github for the code if you are having trouble => https://github.com/deltahedge1/snek. Thanks