On 09:08 am, est...@sindominio.net wrote:
On Sunday 12 April 2009 02:31:29 gl...@divmod.com wrote:
This is a long and terrible road you're about to start down. It's hard
for me to explain exactly why it's so painful, but trust me, it is.

No, I won't trust you :-) I hope you don't take what I'm going to say too
seriously, but you couldn't sound more patronising.

Sorry for that; it certainly wasn't my intention. I meant just what I said: I find it difficult to communicate about. Perhaps I should have said, "trust me, my experience suggests that it is".
Anyway, if it's so hard to explain, it's probably harder to understand, and thus, it's something that you cannot figure out by yourself. So, if you don't
mind, why do you think it's so bad?

It's hard to explain because it's hard to describe a full use-case. My experiences with adapter registries are diffuse, so I can't put it all together into one nice concrete example; just a bunch of places where I thought maybe I wanted multi-adaptation, started in that direction, and then realized that I actually wanted something else when it made a mess. A concrete description of one of these use-cases would probably end up talking more about email or templating than about interfaces.

Abstract examples tend to proliferate metasyntactic variables, so it's not clear what the relationships are of all the entities. In fact, it may be that I'm not understanding *your* use-case because "IFooFactory" isn't a particularly expository name :). But let me give it a go.

Multi-adapters are problematic because they specify a collection of objects, but they don't specify the relationship between those objects. When you adapt a model/request pair to a view in Zope, how do you know which one's the model and which one's the request? If multiadapt(IFoo, (a, b)) works, should multiadapt(IFoo, (b, a))? If not, why not? If so, why so? Is the position of the parameters significant? Does it imply something about the role that each component plays to its adapter? What about multiadapt(IFoo, (b, b, a))? I know that zope.interface has answers to all these questions (and indeed, in most cases I think I know what those answers are) but they're very hard to figure out from first principles; they're kind of arbitrary, whereas other edge-case rules in z.i, for example, the resolution order of adapters in inheritance relationships make perfect sense to me.

Maybe if you use zope.component it works out. I don't have much experience with it, but I do know that it gives you a lot more options in terms of managing what adapters get used when, and by whom. Adapters also have names, which gives you another level of disambiguation.

But in the context of Twisted's adapter registry, you just have one global registry. When you're using this for f(x)->y type relationships, it provides a reasonable way for one system to hook into another, because system X can receive a bar when it only knows about foos, and say IFoo(bar) and that works because of an adapter registered by the system that declared bar. But when you start putting additional arguments in there, it's not clear how those different systems will handle them.

For example, if you want a system that accepts "configuration", you can register an adapter from a foo, username, and password, to produce a bar. i.e. (IFoo, str, str) -> IBar. Later, what if we want an adapter from a foo, hostname and path? (IFoo, str, str) is already taken.

But, if you do this by explicitly declaring an intermediate interface, it's much clearer. IBarFactory(foo).configureUsernamePassword(username, password)->Bar makes the placement of the responsibility very clear; later, you could add IBarFactory(bar).configureHostnamePath(hostname, path)->Bar.

And this is why the explanation of its wrong-ness is difficult. I know that you could get the same effect by doing (IFoo, IUsernamePassword) -> IBar and (IFoo, IHostnamePath).
So the problem is that the resulting factory is not complete, it doesn't have enough information to build protocols. The only workaround is to explicitly
set those extra parameters after it has been instantiated.

Would the specifics of your application allow you to introduce an intermediary IProtocolFactoryConfigurer interface, with .config* methods that would return the actual ProtocolFactory, as with my IBarFactory above? "IBar" here would be IProtocolFactory; sorry for the confusion with the word "factory".
Although if I misunderstand the spec of
"IFooFactory" and it *does* include a bar attribute, nevermind.

The IFooFactory interface declares that attribute, but it's not immediately
available when the service is adapted into a factory.

I fail to see what's the difference between bar= and configureBar() in this
particular case.

Yeah, when I said "[if] it *does* include a bar attribute, nevermind", that's what I meant; I misunderstood and thought that the attribute was part of a different interface or concrete type. Again, If .configureBar() *returns* a protocol factory, then maybe it's better.
If you want something more magical and adapter-ish, them maybe you want
this:

IFooFactory(ConfigurationObject(myService, bar="some value"))

I think that solution is even worse. That particular instance of
ConfigurationObject is tied to myService: it can't be (de)serialized, reused
with other adapters, ConfigurationObject and MyService are completely
unrelated and I don't think the former should depend on the latter.

So ConfigurationObject is purely for the IFooFactory implementation?
>In order to implement this, registerAdapter would have to be able to
>take a
>tuple of interfaces,
(...)
>would that feature make sense? If so, I'll file an issue right now :-)

Unfortunately, much as I've been encouraging people to file tickets
lately, no :).  I don't think it makes sense.

I still think it does, but nobody wants to make the ticket count grow
higher :-) Anyway, Zope already has this nifty thing called multi- adapters,
which implement exactly what I described:

http://www.muthukadan.net/docs/zca.html#multi-adapter

Thanks for that link, by the way, that's a great introduction to zope.component. Not one I'd seen before.
but I always liked Twisted's adapter registry better and our application already uses it in quite a few places. I like being able to call IFoo(bar) and let the registry lookup an adapter automatically, instead of having to
call getAdapter()/queryAdapter()

You'd still have to call something like getAdapter or queryAdapter in order to use multi-adapters with Twisted's registry. IFoo((a, b, c)) will adapt from "tuple" to IFoo, and it would be ambiguous to change it to work otherwise - a number of existing systems do register adapters for tuples. IFoo(a, b, c) can't be made to work because InterfaceClass.__call__'s signature can't be modified to support it, since IFoo(a, b) means "use b as default".

Actually... I got curious about the implementation work required, and I realized that no modification to Twisted is really necessary, if what you want is this syntactic convenience. The registry used by Twisted already supports multi-adapters (since it's just a zope.interface registry), and is already explicitly exposed publicly so you can do zope-interface-y stuff to it directly.

I'm still not a big fan of multi-adapters, but the code's so short, and so non-invasive (in particular I don't believe it'll affect the performance of "normal", single adaptation), that I probably wouldn't argue too hard against it. Feel free to file that ticket, although I won't promise that somebody *else* won't come along and object. In any case, you can start using it right away if it suits you :).

Anyway, I hope this is useful:

# multireg.py

from zope.interface.declarations import implementedBy

from twisted.python.components import getRegistry

def registerMulti(adapter, fromInterfaces, *toInterfaces):
   registry = getRegistry()
   for interface in toInterfaces:
       registry.register(fromInterfaces, interface, '', adapter)
       registry.register([implementedBy(multi)], interface, '',
                         translateMulti(interface, registry))

def translateMulti(toInterface, registry):
   def translator(multiple):
       return registry.queryMultiAdapter(
           multiple.conformers, toInterface)
   return translator

class multi(object):
   def __init__(self, *conformers):
       self.conformers = conformers

# and here's an example:

from zope.interface import Interface, implements

class IA(Interface): ""
class IB(Interface): ""
class IC(Interface): ""

class A(object): implements(IA)
class B(object): implements(IB)

class ABC(object):
   implements(IC)
   def __init__(self, a, b):
       self.a = a
       self.b = b

registerMulti(ABC, [IA, IB], IC)

print IC(multi(A(), B()))
print IC(multi(1, 2), 3)

# -glyph

_______________________________________________
Twisted-Python mailing list
Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python

Reply via email to