[Baypiggies] Entry points help

Jake Alheid shakefu at gmail.com
Wed Feb 13 03:41:37 CET 2013


If you want 3rd parties to be able to register runners, then entry points
is a good way to go. However, since your example looks like you have
control over all the files, you might want to look at runpy (
http://docs.python.org/2/library/runpy.html#runpy.run_module) to import
your runner's module dictionary, which can then be called. Something like:

def get_runner(name):
    return runpy.run_module('baypiggies.' + name)

get_runner('fruit')['chew']()

The documentation is really terrible for setuptools... I can't remember
what I originally read that helped me grok what was going.

If you really want to use entry points for this, you want to define it in
your setup.py, and do something like:

entry_points = {
    'baypiggies':[
        'fruit = baypiggies.fruit',
        'meta = baypiggies.meat',
        'mock = baypiggies.mock',
    ],
},

This just registers those namespaces (modules in this case) with
setuptools/pkg_resources for later consumption. You consume using
iter_entry_points:

def get_runner_module(name):
    """ Return a module for `name`. """
    # This looks for the first entry point with `name` in the 'baypiggies'
    for entry in pkg_resources.iter_entry_points('baypiggies', name):
        # Return the loaded module or object, or raise ImportError
        return entry.load()

If you don't want the module in sys.modules, use runpy instead, and replace
the last line with:

        # Return the module's namespace dictionary
        return runpy.run_module(entry.module_name)

Of course, using entry_points will also allow third party packages to
register with your runner using the 'baypiggies' entry point. You could
allow for that, and get a list of all registered names, like:

def get_registered_names():
    names = []
    for entry in pkg_resources.iter_entry_points('baypiggies'):
        names.append(entry.name)
    return names

Hope this helps!
--
Jake Alheid
http://about.me/jake


On Tue, Feb 12, 2013 at 5:00 PM, Glen Jarvis <glen at glenjarvis.com> wrote:

> I have a directory like this:
>
> baypiggies
>    - __init__.py
>    - fruit.py
>    - meat.py
>    - mock.py
>    - runner.py
>
> My runner.py is the main entry point and it is used to dynamically chose
> which of these other files to import.
>
> # pylint: disable=C0103,R0904
>
> """A sample dynamically loaded example with endpoints
>
> ./runner.py --backend=fruit
> """
>
> from optparse import OptionParser
>
>
> entry_points = {
>     'fruit': dict(
>         thump = ('fruit', 'thump'),
>         eat = ('fruit', 'eat'),
>         chew = ('fruit', 'chew'),
>     ),
>     'meat': dict(
>         thump = ('meat', 'thump'),
>         eat = ('meat', 'eat'),
>         chew = ('meat', 'chew'),
>     ),
>     'mock': dict(
>         thump = ('mock', 'thump'),
>         eat = ('mock', 'eat'),
>         chew = ('mock', 'chew'),
>     ),
>     'custom1': dict(
>         thump = ('myns.mypkg.mymodule', 'thump'),
>         eat = ('myns.mypkg.mymodule', 'eat'),
>         chew = ('myns.mypkg.mymodule', 'chew'),
>     ),
> }
>
> def import_entry_points(entries):
>
>     """Dynamically import the functions for the specified backend
>
>     entry_points is a global dictionary whos keys correspond to each
>     of the different types of backends that we can support. The variable
>     options.backend specifies which of the backends that will be used
>     during this program run.
>
>     The value of entry_points (for options.backend) is another
>     dictionary which map the functions needed to the modules from where
>     we will import these modules. We only want to import the backend
>     modules that will be used (and not have unnecessary dependencies).
>
>     This module will replace the values in this inner dictionary with
>     the imported functions. This way, the functions are imported and
>     available when needed.
>     """
>
>     for entry in entries:
>         module, name = entries[entry]
>         _temp = __import__(module, globals(), locals(), [name], 0)
>         entries[entry] = getattr(_temp, entry)
>
>
> def run(backend="mock"):
>     print "Running, backend: ", backend
>     import_entry_points(entry_points[backend])
>
>     import pprint
>     pprint.pprint(entry_points)
>
>     print "THUMPING..."
>     entry_points[backend]["thump"]()
>
>     print "EATING..."
>     entry_points[backend]["eat"]()
>
>     print "CHEWING..."
>     # Chew five times
>     entry_points[backend]["chew"](5)
>
> if __name__ == "__main__":
>     parser = OptionParser()
>     parser.add_option("-b", "--backend", dest="backend",
>                       default="mock",
>                       help="Choose which backend to run.")
>
>     (options, args) = parser.parse_args()
>     run(options.backend)
>
>
>
> Now, as you can see, the backends are loaded dynamically depending upon
> the command line options (let's only import what we need).
>
> prompt> python runner.py
> Running, backend:  mock
> {'custom1': {'chew': ('myns.mypkg.mymodule', 'chew'),
>              'eat': ('myns.mypkg.mymodule', 'eat'),
>              'thump': ('myns.mypkg.mymodule', 'thump')},
>  'fruit': {'chew': ('fruit', 'chew'),
>            'eat': ('fruit', 'eat'),
>            'thump': ('fruit', 'thump')},
>  'meat': {'chew': ('meat', 'chew'),
>           'eat': ('meat', 'eat'),
>           'thump': ('meat', 'thump')},
>  'mock': {'chew': <function chew at 0x10c3e1aa0>,
>           'eat': <function eat at 0x10c3e1a28>,
>           'thump': <function thump at 0x10c3e19b0>}}
> THUMPING...
> Pretend to thump
> EATING...
> Pretend to eat
> CHEWING...
> Prentend to chew
>
>
> And, totally new/different backend if I choose the option:
>
>
> Running, backend:  fruit
> {'custom1': {'chew': ('myns.mypkg.mymodule', 'chew'),
>              'eat': ('myns.mypkg.mymodule', 'eat'),
>              'thump': ('myns.mypkg.mymodule', 'thump')},
>  'fruit': {'chew': <function chew at 0x103a11aa0>,
>            'eat': <function eat at 0x103a11a28>,
>            'thump': <function thump at 0x103a119b0>},
>  'meat': {'chew': ('meat', 'chew'),
>           'eat': ('meat', 'eat'),
>           'thump': ('meat', 'thump')},
>  'mock': {'chew': ('mock', 'chew'),
>           'eat': ('mock', 'eat'),
>           'thump': ('mock', 'thump')}}
> THUMPING...
> Thumping fruit...
> EATING...
> Eating fruit.. very healthy....
> CHEWING...
> Fruit chew  0
> Fruit chew  1
> Fruit chew  2
> Fruit chew  3
> Fruit chew  4
>
>
>
> Here are examples of my backends:
>
>
>
>
> """An empty mock (Currently not implemented)"""
>
>
> def thump():
>
>     print "Pretend to thump"
>
>
> def eat():
>
>     print "Pretend to eat"
>
>
> def chew(number_of_times):
>
>     print "Prentend to chew"
>
>
>
>
> Here's the mock one (by default):
>
>
> def thump():
>
>     print "Pretend to thump"
>
>
> def eat():
>
>     print "Pretend to eat"
>
>
> def chew(number_of_times):
>
>     print "Prentend to chew"
>
>
>
>
>
> So, I'm supposed to be using entry points from the setuptools library
> (instead of the above). But, I've googled and get stuck. I just don't see
> how to use it (and use it without doing a setup each time).:
>
>
> http://stackoverflow.com/questions/774824/explain-python-entry-points/9615473#9615473
>
> Can someone help me by example? I'm confused...
>
>
> Cheers,
>
>
> Glen
> --
>
> "Pursue, keep up with, circle round and round your life as a dog does his
> master's chase. Do what you love. Know your own bone; gnaw at it, bury it,
> unearth it, and gnaw it still."
>
> --Henry David Thoreau
>
> _______________________________________________
> Baypiggies mailing list
> Baypiggies at python.org
> To change your subscription options or unsubscribe:
> http://mail.python.org/mailman/listinfo/baypiggies
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/baypiggies/attachments/20130212/b6ccc39a/attachment-0001.html>


More information about the Baypiggies mailing list