[Zope3-dev] Re: Interface declaration API
Phillip J. Eby
pje@telecommunity.com
Wed, 12 Mar 2003 15:43:53 -0500
This e-mail actually contains API-level questions/issues, along with one
very brief implementation comment in response to a question of yours...
At 02:54 PM 3/12/03 -0500, Jim Fulton wrote:
>It won't fail because it will get an attribute error and return an
>empty interface spec. Of course, at that point, mutation will be difficult.
>Mutation won't, in general, be possible through security proxies.
While on this subject, can you or someone else involved with the original
proposal explain why it's useful to have mutable interface specs,
anyway? Is there some highly motivating use case for having interface
specs be able to be lvalues? Otherwise, it seems much cleaner to me to
have API functions to do the manipulating. That is,
'implementedBy(Foo).add(IBar)' would be 'addImplements(Foo, IBar)'.
>>With descriptors only, you are powerless to do this to anything that
>>doesn't have the descriptor already present.
>
>When you need one, you can add one. For example:
>
> classInstanceInterfaces(Foo).set(IFoo)
>
>would add a descriptor to Foo.
Why is this more desirable than say, 'setImplements(Foo, IFoo)'?
> > Data descriptors take
>>precedence over what's in __dict__, and this can make metaclass access
>>"weird". Example:
>>class Meta(type):
>> aProp = property(...)
>>class Class(object):
>> __metaclass__ = Meta
>> aProp = property(...)
>>ob = Class()
>>In the above code, accessing 'ob.aProp' will call the __get__ of
>>Class.__dict__['aProp'], but accessing 'Class.aProp' will call the
>>__get__ of Meta.__dict__['aProp']. This means that you can't just write
>>a '__get__' that looks to see whether it's being called on the instance
>>or the class in that case.
>
>Why not? In both of the cases you've mentioned, we know we are being called
>on the "instance" as far as the descriptor is concerned.
Right, but the descriptor *itself* cannot be the bearer of the data,
because the metaclass descriptor won't know the instance's content. That
is, Meta.aProp cannot know what value Class.aProp should have without
consulting some other data present in Class. So, you have to have two
attributes: the "real" attribute, and the descriptor that either computes
or returns the value of the "real" attribute. But this is implementation
detail.
>>Actually, by having InterfaceSpecification instance simply keep a set or
>>dictionary of the declared interfaces and have a __contains__ method that
>>delegates to the dictionary, you wouldn't need an explicit cache:
>>def isImplementedBy(self,ob):
>> return self in implementedBy(ob.__class__) or self in
>> directlyProvidedBy(ob)
>
>That doesn't work, because you need to do extends checks, not just sameness
>checks.
>
>Consider:
>
> class I1(Interface): pass
> class I2(I1): pass
>
> class C: implements(I2)
>
> I1.isImplementedBy(C())
>
>The answer needs to be true, even though I1 isn't in the interface declaration
>for I2.
>
>Of course, the interface specification could include a set of the interfaces
>in the specification and all of the interfaces that they extend.
Sorry; I meant to state the latter, but it got left out.
This brings me to another API-level question...
Is it necessary or desirable to have IInterfaceSpecifications represent
__iter__() differently from flattened()?
That is, given:
class I1(Interface): pass
class I2(I1): pass
spec = InterfaceSpecification(I2)
is it necessary that:
list(spec) == [I2]
Or is it sufficient if:
I2 in list(spec)
That is, is it acceptable for an InterfaceSpecification to simply list its
contents in flattened form?
The current (outdated) spec on the Wiki says that __contains__ returns true
"if the given interface is one of the interfaces in the specification and
false otherwise". This doesn't address extends().
My own inclination is that the most desired semantics of
IInterfaceSpecification are actually as follows:
class IInterfaceSpecification(Interface):
def __iter__():
"""Iterate over all IInterface objects that would be considered a
member of this spec
by __contains__()"""
def __contains__(interface):
"""Return true if 'interface' is a member of any specification
aggregated by this one, or is this specification. 'interface' must
implement IInterface."""
class IInterface(IInterfaceSpecfication):
...
By implication, therefore, 'extends()' would not then be strictly necessary
in IInterface, since 'IFoo in IBar' would be true if IBar is or extends
IFoo. (It might be useful to keep it around anyway.)
If an interface spec (declaration? set?) doesn't have to distinguish
between iter() and flattened(), its semantics are much simpler, and its
__iter__/__contains__ are now symmetrical. Implementing mutable interface
specs is also cleaner, because they can simply loop over the things being
added to them, and add them to an internal dictionary.
This brings to light another point of unclear semantics in the current
spec, as I understand it. The 'add()' and 'remove()' methods do not say
anything with respect to extends()-ness. That is:
s1 = InterfaceSpecification(I2)
s1.add(I1) # is I1 added? it is, after all "contained" by s1 already...
s1.remove(I1) # now what happens?