Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I add a setuptools entry_point as an example in my main project?

I want to make my program pluggable. I'd like to use the setuptools way of doing so, using eggs.

I've been able to make a plugin to provide an alternate class for some functionality, and I can use it.

I would like to select the class to use at runtime; either the one in my core module or in any of the plugins. I'd like to use the pkg_resources way of querying for these classes:

for entrypoint in pkg_resources.iter_entry_points("myapp.myclasses"):

How can I create an EntryPoint object for my class in core, and register it, so that iter_entry_points will return it the same way as for my .egg plugin class ?

like image 433
Thomas Vander Stichele Avatar asked Nov 24 '12 20:11

Thomas Vander Stichele


1 Answers

pkg_resources.iter_entry_points lists any entry points by the given name in any egg, including your own package. Thus, if your entry_points entry in setup.py lists the following, and you've run setup.py develop to generate the metadata, your own entry point will be included:

[myapp.myclasses]
classentry1 = myapp.mymodule:myclassname1
classentry2 = myapp.mymodule:myclassname2

The Babel project does exactly this; in it's setup.py it lists entry points for both babel.checkers and babel.extractors, and these are looked up by babel.messages.checkers:_find_checkers and babel.messages.extract:extract, respectively.

If you do not want to have a setup.py file (which is easy enough to create and/or generate from a template), then you are facing having to alter the internal state of pkg_resources.working_set instead:

  • working_set.entries is a list of eggs. You'll have to add the path of your project's top-level directory to this.
  • working_set.entry_keys is a mapping from paths in entries to a list of package names. Add your project as `working_set.entry_keys[path] = ['package.name']
  • working_set.by_key is a mapping from package name to a pkg_resources.Distribution instances. You'll need to create a Distribution instance and store it under your package name: working_set.by_key['package.name'] = yourdistribution.

For your purposes the Distribution instance can be fairly sparse, but I'd include at least the project name. You'll need to have a entry-point map on it though:

yourdistribution = Distribution(project_name='package.name')
yourdistribution._ep_map = {'myapp.myclasses', {
    'classentry1': entrypointinstance_for_classentry1,
    'classentry2': entrypointinstance_for_classentry2,
}}

The internal structure _ep_map is normally parsed on demand from the egg-info metadata.

Please note that this is relying entirely on undocumented internal data structures that can change between versions. In other words, you are on your own here. I'd generate a setup.py file instead, and run python setup.py develop to generate the egg metadata instead.

like image 138
Martijn Pieters Avatar answered Sep 21 '22 05:09

Martijn Pieters