[Zope3-dev] Re: practice and theory: event service and connotations of "implementing an interface"

Steve Alexander steve@cat-box.net
Fri, 20 Dec 2002 09:08:00 +0000


> Through subclassing, the global event service claims to implement
 > subscribable, yet raises a NotImplementedError when one of its
 > methods, subscribe, is called.
> 
> I understand the practical reasoning for this, and am trying with
 > Steve and Casey to come up with a solution for the practical
 > issues (see second half below for a new proposal).
> 
> But more theoretically, can an object truly claim to implement an
 > interface merely by having the method name, even if it does not
 > live up to the descriptive contract?  This seems like a bad precedent.

I'll excplain this at more length below. Basically, you're right in this:

 > Can an object truly claim to implement an interface merely by having
 > the method name, even if it does not live up to the descriptive
 > contract?

However, that is not the case with a method raising an exception. 
Raising an exception is always a valid alternative to doing the work 
requested.

If you'd said:

   Can an object truly claim to implement an interface if it always
   raises an exception in response to a calling a method given in the
   interface?

Then the answer is "yes".


Longer explanation:

When a component advertises an operation in an interface, it does not 
guarentee that the operation will always succeed. Often, whether or not 
the operation will be successful depends on the internal state of the 
object being operated upon.

Anything can raise an exception.

In the case of the GlobalEventService, the 'subscribe' operation will 
always be unsuccessful. I'm fine with this, and I think it is a valid 
fulfilment of the interface.

Now, if you called 'subscribe' on an ISubscribable and it did something 
entirely different, like created a whole bunch of objects, or erased 
your hard disk, or aborted all http connections, (add other outrageous 
things here), and still didn't subscribe the client as requested, then 
that would not be a valid fulfilment of the interface

I could make an ICreateBunchOfObjectsSubscribabale interface that would 
either subscribe and create a whole bunch of objects, or raise an 
exception and not subscribe and not create any objects.

Implementing an interface says to clients "You may call this method, 
which will be understood as this kind of request, with these arguments". 
Like all requests, a request may be declined.

You could write an interface that describes a request that can never be 
declined, but that would be a pretty special interface.

In the case of the GlobalEventService, it is a fault in the overall 
configuration of your application if you try to subscribe a local object 
to it. Raising an exception is quite appropriate here.


>  It will override the subscribe method as follows:
> 
>     def subscribe(subscriber, event_type=IEvent, filter=None):
>         """As Zope.Event.ISubscribable.  Will raise a SubscriptionError
>         if the subscriber is wrapped.
>         """

That won't do. A subscriber doesn't have to be wrapped to be a local 
object. Only global objects may subscribe to the global event service.
An object is either local or global.

At the sprintathon, I talked at some length with Jim about the 
GlobalEventService. Making it have a globalSubscribe method was the best 
we could come up with. I'm open to other ideas, though.

Here are the constraints:

* There must be no way for a local object inadvertantly to subscribe to 
the global event service.

* The global event service must implement the interface declared for the 
"Events" serviceType.

Also, there is no obvious way to distinguish between a local object and 
a global object by inspecting the object.

The global event service *could* implement 'subscribe' by trying to get 
a location for the subscriber and failing if it can get a location. But, 
this still doesn't stop a local subscriber that is not properly wrapped 
(for whatever reason) from subscribing globally.

Having 'subscribe' raise an exception, and having a separate 
globalSubscribe method is easy to understand, simple to implement, and 
has no loop-holes.


> The built in implementation will always try first to store the object 
> via the hubid service, and failing that will use an absolute path.

This is too implicit. I think the local event service should subscribe 
by path by default with the 'subscribe' method. It should be easy to 
subscribe by hubId, but it should be something explicit.

One reason for this is that it is important to know if you have 
successfully subscribed by hubId. This allows you to move your 
subscriber around without changing your subscription. The implicit 
fall-back you describe doesn't give this feedback.

--
Steve Alexander