[Baypiggies] Entry points help

Glen Jarvis glen at glenjarvis.com
Wed Feb 13 03:56:01 CET 2013


Your example and slides are going to help (I'm on my way home on the bus so not hacking on it for the moment).

I'll check out the docs after I look at your slides and example. Again, awesome help :)

G

On Feb 12, 2013, at 6:49 PM, Jake Alheid <shakefu at gmail.com> wrote:

> Ooh, I found some of the elusive documentation: http://pythonhosted.org/distribute/pkg_resources.html?highlight=iter_entry_points#convenience-api
> 
> Good luck!
> --
> Jake Alheid
> http://about.me/jake
> 
> 
> On Tue, Feb 12, 2013 at 6:47 PM, Glen Jarvis <glen at glenjarvis.com> wrote:
>> I'm still reading this and need to play with your examples. But, I think this is EXACTLY what I needed.
>> 
>> Thank you!!!!
>> 
>> G
>> 
>> On Feb 12, 2013, at 6:41 PM, Jake Alheid <shakefu at gmail.com> wrote:
>> 
>>> 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/6e9d0468/attachment-0001.html>


More information about the Baypiggies mailing list