Martin Aspeli wrote:
We need to make sure that we're not inventing a different way to achieve something which is already possible. This will lead to confusion, because people will have to know "which way" is applicable in a given situation, and the distinction will seem arbitrary.
I fear we are indeed inventing a different way to achieve something which is already possible. We aren't doing it arbitrarily, though: the current way just requires the use of an interface instead of a string. Interface usage for such a simple pattern implies a cognitive load that appears to exceed the pain point of most Python developers who are not already familiar with Zope. So we'd like to ameliorate that as best we can.
In a system like this, there are no interfaces; the string 'root_factory' performs the same job as the IRootFactory interface for registration and lookup. I'd like to make the ZCA registry operate like this. There's really no reason for there to be an interface hanging around to represent this thing: we're using the ZCA as a complicated dictionary here.
I think there is a reason, though you may not agree it's a good one. The interface makes a promise about what the component is supposed to be able to do. We don't enforce that in our duck-typing programming language, but I think there is value in being able to say, "I want an object that conforms to this interface (i.e. delivers what the interface promises) - please get me the best one you've got".
That is indeed the promise. But obviously the object you get back needn't *actually* implement the interface you asked for; it's only conventional. It is the same with a dictionary lookup: if you document that, for your application, reg['root_factory'] will return an object implementing IRootFactory, it's pretty much equivalent as far as I can tell. Use of the ZCA API to store and retrieve instances implementing an interface isn't required to get benefit out of the existence of that interface.
It would also obviously be possible to just add a dictionary instance attribute to a registry, so instead of subclassing Components from dict, you might do:
reg = getSiteManager() reg.simple['root_factory'] = root_factory
To be honest, I don't mind one way or another; I'd just like to push whatever we do upstream if possible. If we move too far away from the stock ZCA facilities, it becomes harder to integrate Zope apps into BFG and vice versa.
I whole-heartedly agree, and I think it's important that we use the momentum behind BFG (and other consumers of the ZTK) to drive the ZTK forward. Anything else would be stupid.
I'm still concerned that your proposal basically leaves us with two ways of implementing the singleton pattern with the ZCA, and I'm not sure that's in our best interest. I'd be interested to hear your thoughts further, though.
I guess I can only point at the rationales in <http://docs.repoze.org/bfg/1.1/designdefense.html#bfg-uses-the-zope-component-architecture-zca>
Off the top of my head, another way to think of this *might* be to say that the 'dict access' is basically looking up a *named* utility providing a very generic marker interface, e.g. zope.component.interfaces.IUtility or even just zope.interface.Interface. That way reg['foo'] == getUtility(IUtility, name='foo'). Obviously, assignment would register in the same way.
I'm not sure it's "better", though. :)
That would also be fine, and it would normalize things a bit, although the implementation would be harder and it would result in slower lookups. But if it made folks feel better than inheriting from dict, I'd be +1 on it. - C