[Baypiggies] Entry points help
Glen Jarvis
glen at glenjarvis.com
Wed Feb 13 03:47:56 CET 2013
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/1f154de9/attachment.html>
More information about the Baypiggies
mailing list