At 02:12 PM 7/24/2005 -0500, Ian Bicking wrote: >This kind of addresses the issue where the module structure of a package >becomes an often unintentional part of its external interface. It feels a >little crude in that respect... but maybe not. Is it worse to do: > > from package.module import name > >or: > > name = require('Package').load_entry_point('service_type', 'name') > >OK, well clearly the second is worse ;) But if that turned into a single >function call: > > name = load_service('Package', 'service_type', 'name') > >It's not that bad. Maybe even: > > name = services['Package:service_type:name']
The actual API I have implemented in my CVS working copy is: the_object = load_entry_point('Project', 'group', 'name') which seems pretty clean to me. You can also use dist.load_entry_point('group','name') if you already have a distribution object for some reason. (For example, if you use an activation listener to get callbacks when distributions are activated on sys.path.) To introspect an entry point or check for its existence, you can use: entry_point = get_entry_info('Project', 'group', 'name') which returns either None or an EntryPoint object with various attributes. To list the entry points of a group, or to list the groups, you can use: # dictionary of group names to entry map for each kind group_names = get_entry_map('Project') # dictionary of entry names to corresponding EntryPoint object entry_names = get_entry_map('Project', 'group') These are useful for dynamic entry points. >Though service_type feels extraneous to me. I see the benefit of being >explicit about what the factory provides, but I don't see the benefit of >separating namespaces; the name should be unambiguous. You're making the assumption that the package author defines the entry point names, but that's not the case for application plugins; the application will define entry point names and group names for the application's use, and some applications will need multiple groups. Groups might be keyed statically (i.e. a known set of entry point names) or dynamically (the keys are used to put things in a table, e.g. a file extension handler table). >>In addition to specifying the entry point, each entry in the import map >>could optionally list the "extras" that are required if that entry point >>is used. >>It could also issue a 'require()' for the corresponding feature if it has >>any additional requirements listed in the extras_require dictionary. > >I figured each entry point would just map to a feature, so the >extra_require dictionary would already have entries. The problem with that is that asking for a feature that's not in extras_require is an InvalidOption error, so this would force you to define entries in extras_require even if you have no extras involved. It would also make for redundancies when entry points share an extra. I also don't expect extras to be used as frequently as entry points. >>So, I'm thinking that this would be implemented with an entry_points.txt >>file in .egg-info, but supplied in setup.py like this: >> setup( >> ... >> entry_points = { >> "wsgi.app_factories": dict( >> feature1 = "somemodule:somefunction", >> feature2 = "another.module:SomeClass [extra1,extra2]", >> ), >> "mime.parsers": { >> "application/atom+xml": "something:atom_parser [feedparser]" >> } >> }, >> extras_require = dict( >> feedparser = [...], >> extra1 = [...], >> extra2 = [...], >> ) >> ) > >I think I'd rather just put the canonical version in .egg-info instead of >as an argument to setup(); this is one place where using Python >expressions isn't a shining example of clarity. But I guess this is fine >too; for clarity I'll probably start writing my setup.py files with >variable assignments, then a setup() call that just refers to those variables. The actual syntax I'm going to end up with is: entry_points = { "wsgi.app_factories": [ "feature1 = somemodule:somefunction", "feature2 = another.module:SomeClass [extra1,extra2]", ] } Which is still not great, but it's a bit simpler. If you only have one entry point, you can use: entry_points = { "wsgi.app_factories": "feature = somemodule:somefunction", } Or you can use a long string for each group: entry_points = { "wsgi.app_factories": """ # define features for blah blah feature1 = somemodule:somefunction feature2 = another.module:SomeClass [extra1,extra2] """ } Or even list everything in one giant string: entry_points = """ [wsgi.app_factories] # define features for blah blah feature1 = somemodule:somefunction feature2 = another.module:SomeClass [extra1,extra2] """ This last format is more readable than the others, I think, but there are likely to be setup scripts that will be generating some of this dynamically, and I'd rather not force them to use strings when lists or dictionaries would be more convenient for their use cases. Anyway, I hope to check in a working implementation with tests later today. Currently, the EntryPoint class works, but setuptools doesn't generate the entry_points.txt file yet, and I don't have any tests yet for the entry_points.txt parser or the API functions, although they're already implemented. _______________________________________________ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com