[PEAK] Making PyProtocols work for aeve
Phillip J. Eby
pje at telecommunity.com
Tue Nov 25 11:54:55 EST 2003
At 10:45 AM 11/25/03 -0500, Bob Ippolito wrote:
>I have a project that I'm thinking about using PyProtocols for.
>Currently, it's using something conceptually similar that I wrote
>myself. The software is aeve, an Apple Events bridge for MacPython.
>The way you communicate with the Apple Event subsystem is via this mostly
>opaque type called an AEDesc (which is kinda like a PyObject*).
>An AEDesc has a 4 character code that represents what it is, and then some
>(usually) opaque bytes.
>
>Some AEDescs are simply decoded into Python types, for example lists,
>floats, records (like a dict, except the keys are always 4 character
>codes), etc. However, some of them represent classes. A class code is
>specific to a particular application, not global to the Apple Event
>subsystem (as in floats, ints, etc.). So when these are adapted to Python
>types (there is a Python "shadow" of the class that mimics all of the
>properties, methods, etc) they must be adapted in the context of the
>application (or the global context, if not found in the application). One
>problem is that you can get these class types inside container AEDescs, so
>the adaptation needs to carry around a context with it.
>
>I've been having the following conceptual issues making this fit into
>PyProtocols
Just a foreword here... it's kind of difficult for me to make clear
recommendations, because I don't know enough about the use case of
this. That is, what kind of API you want to expose to the user of these
events.
For example, do you want to let people declare adaptations from
AEDesc.types? Or is that going to be hidden behind some other interface
you're creating?
Anyway, there are many ways to address your questions, and the "right"
answers will depend a lot on what you're trying to do.
>1) I really need to adapt by object introspection (The AEDesc.type four
>character code), not by type. Everything is an AEDesc on the incoming
>side. Right now I'm playing around with the idea that I make interfaces
>for every since four character code that I know about, and an AEDesc
>wrapper that figures out which interface to use (based upon the
>application context) and does adviseObject(self,
>provides=(ISomeSpecificFourCharCode,)) in its __init__. This is kinda ugly.
The first thing that comes to mind here is using a URI namespace for these,
but I'm guessing you already thought of that.
>2) I need adaptation contexts (per bridged application). I don't really
>see how the "Extending the Framework for Context" example can apply to my
>use case.
I kind of need to understand what "per bridged application" means. I
suspect that contextual protocols *will* suffice for what you want, but
it'll be a bit more complex due to having to deal with URIs at the same time.
So far, I see an architecture that looks like
'ctx.protocolForDesc("ABCD")', where 'ctx' is one of your application
contexts. 'ctx' simply keeps a cache of type codes to protocol
instances. This could be as simple as:
protocols = binding.Make(dict)
def protocolForDesc(self,code):
try:
return self.protocols[code]
except KeyError:
proto = protocols.Protocol()
self.protocols[code] = proto
return proto
Then, as you described, created event objects would then:
adviseObject(self,provides=[ctx.protocolForDesc(self.type)])
If you want to also support a non-contextual version using URI's, you could
do this:
def protocolForDesc(self,code):
try:
return self.protocols[code]
except KeyError:
proto = protocols.Variation(
protocols.protocolForURI(self.URI_BASE+code),
self
)
self.protocols[code] = proto
return proto
Then, adapters registered from the URI protocol will apply to contexts that
don't have a context-specific adapter registered.
>3) I need to be able to weight certain adaptations. Some conversions are
>lossy (i.e. double -> float), and shouldn't be used unless that's what's
>asked for.
You can do weighting using the depth indicators, but I'm a little
suspicious of your question here because it seems to be implying that
you're doing something different than what I'm thinking. Clarifying what
you mean would be very helpful here.
>4) If you can't find an explicit adaptation path, you can ask the Apple
>Event subsystem to (attempt to) coerce it for you. This has the problem
>of making way too many adapter declarations.
Coerce it to *what*? I'm a little lost here. Why wouldn't you just let
the user of the bridge define what they want to do for their app's purposes?
>5) I have a "MetaInterface" .. "convert this AEDesc to something
>Pythonic" or "convert this Pythonic thing to an AEDesc". I haven't quite
>figured out how best to set that up with PyProtocols. I'm almost thinking
>that the four character code interfaces should also be the adapters
>to/from the base AEDesc, would that work well with PyProtocols?
Now my head is exploding. Perhaps I'd need to learn something about AEDesc
to be able to understand this.
Then again, maybe all you're saying is that you have 'IPythonic' and
'IAEDesc'. If so, that's a lot simpler. :)
>6) A lot of the adaptation is an actual conversion not a wrapping, so I
>would prefer to use a non-instantiated class (via classmethods) or
>function as the adapter from InterfaceA to InterfaceB. I couldn't figure
>that out, it's pretty easy with Twisted's interfaces though.
There are plenty of examples of that scattered through PEAK and
PyProtocols. Look for 'factoryMethod=' to find classes that use class
methods for conversion, and 'declareAdapter()' calls to find conversion
functions.
Generally speaking, to declare an adapter function, you use:
declareAdapter(func, provides=[IProvideThis], forTypes=[...],
forProtocols=[...])
'forTypes' lists classes the function adapts from, and 'forProtocols' lists
protocols it adapts from.
>7) AEDesc has an equivalent to None ('null'). This seems like it would
>be awfully hard to return from PyProtocols ;) I don't really want to have
>all my interfaces use a getter function just so I can adapt this type to None!
Why wouldn't you have your wrapper system just convert 'null' to None in
the first place? (I.e., why go to the trouble of adapting it?) Indeed,
I'd assume that for types where there is no ambiguity or data loss, you
might as well convert them to Python types in the first place. (Why bother
allocating a structure you're just going to convert anyway?)
>It sounds almost like I need to write some really custom __conform__ and
>__adapt__ special methods, so if I have to go that route, what would using
>PyProtocols really get me?
Compatibility with PEP 246 and systems that use it. Mainly, that means
PEAK and Zope 3.
>Also, are there any relatively complex/large open source projects that use
>PyProtocols other than PEAK/PyProtocols? I want to see how other people
>are using it, the examples in the PyProtocols documentation seem to be too
>trivial to be of much use to me.
Given what you're trying to do, I can see why. You are the first person
who's asked about interfacing to another (non-Python) interface
system. I've interfaced PyProtocols so far only to Python interface
systems (Zope and Twisted's). I'd like to help in any way I can, but so
far I'm still handicapped by not fully understanding your situation or goals.
More information about the PEAK
mailing list