-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 03/20/2011 09:46 AM, Jim Fulton wrote:
> Problem > ======= > > ZTK projects use ZCML too much. Ideally, ZCML should only > have to be used when we want to override something. > > Solution sketch > =============== > > I think we ought to come up with a much cleaner way of defining > default configuration. (Pyramid does this by passing default values in > adapter calls, but I think we can do a lot better than that.) I'm not confident that "better" is achievable for the classic "dependency injection" case (i.e., the default is the one you almost always want, except for unit testing). Typically, I define a utility interface *and* the default implementation in the module which mostly uses it. E.g.:: from zope.interface import Interface from zope.component import queryUtility class ISomePlugPoint(Interface): def __call__(foo, bar): """blah blah""" # Look, Ma! No decorator! def defaultImpl(foo, bar): # DTRT for the normal case def clientFunction(request): impl = queryUtility(ISomePlugPoint, default=defaultImpl) Note the absence extra declarations for 'defaultImpl', and of extra syntax of any kind. In order to maintain good test isolation, I usually avoid memoizing the lookup (e.g. as a module scope variable). unless profiling shows that the lookup ends up on a critical path. This pattern even works outside of testing: if you do the frameworky thing and document how to override the utility as a policy, it becomes a trivial integration point. For adapters, the example is a bit noisier, because the component registry wants to call the factory for you:: from zope.interface import Interface from zope.component import queryAdapter class IAdaptsTo(Interface): def someMethod(): """ blah, blah.""" class DefaultImpl(object): # Note that we require no decorator or advice def __init__(self, context): self.context = context def someMethod(self): return 'whatever' def anotherClient(context, request): adapted = queryAdapter(context, IAdaptsTo) if adapted is None: adapted = DefaultImpl(context) # now use it (Note that memoization isn't a practical optimization doesn't work for adapters.) If we added a 'default_factory' argument to 'queryAdapter', we wouldn't need the 'if' statement, which would make this example as compact as the utility version. Or we could add a 'queryAdapterFactory' API instead, and have 'queryAdapter' use it (what is one more function call between friends? ;) The one downside I can see is giving up on the sugar^Wexpressivity of calling the interface directly -- I guess we could propagate the 'default_factory' argument through to the '__call__' of interface. Note that I *wanted* some extra sugar at one point (doing utility lookup when no arguments were passed to Interface.__call__), but I haven't missed that convenience much since I went on a low sugar diet with BFG / pyramid. > I'd like to see us come up with a "pythonic" way to wire components up > that can be overridden through registration (through zcml or > otherwise). Ideally, the mechanism shouldn't "feel" like > "configuration" but like "programming". My example feels like programming to me: no ZCML, no decorators, and no advice needed up until the point you want to override the normal defaults. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tsea...@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAk2GND0ACgkQ+gerLs4ltQ6ynwCeJ1u/Bk3u8LGxhgR1jk2CFQP3 ZrcAoIuZbFCh2dpB611jKvOlUx48alqo =lvhO -----END PGP SIGNATURE----- _______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org https://mail.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - https://mail.zope.org/mailman/listinfo/zope-announce https://mail.zope.org/mailman/listinfo/zope )