[Zope3-dev] Context and Component Lookup

Jim Fulton jim at zope.com
Mon Aug 11 08:45:36 EDT 2003


Martijn Faassen wrote:
> Jim Fulton wrote:
> 
>>So, I think we have a number of choices:
>>
>>- If the author of A knows it's needs are special, it can arrange
>>  for A to supply it's own own context when looking up the adapter
>>  it needs.  In this case, it takes the chance that it will not
>>  use an adapter available where it's used, but it may not care.
>>
>>- If the author of A knows it has this need and that the need
>>  might or might not be filled where it's used, then it can
>>  first try to get the adapter using the context in which it's used,
>>  and uses it's own context if that doesn't work:
>>
>>    a = queryAdapter(self, IBar)
>>    if a is None:
>>       a = getAdapter(self, IBar, context=self)
>>
>>- If the author of A has documented what A's requirements are,
>>  then he might just decide that people who use A without providing
>>  what it needs deserve to lose.  This is the implest approach in
>>  many ways, although, of course, it has it's drawbacks.
> 
> 
> This is a nice overview.
> 
> Is a possible drawback that the component found wants to look up
> other components that you need to satisfy as well?

That is the third case above.  This is a constraint in using the object.
Constraints are drawbacks. :)


 > I mean, could this
> be different dependent on which component is found, or is this actually
> not a worry as which components are found (that can find other components)
> are under control of the calling context.
> 
> Anyway, I'm still slightly concerned that the implicit behavior of
> depending on the request's context is the default, instead of the
> attempt to depend on the local context..

Attempting to depend on the local context is just as implicit.
The default behavior is implicit in any case, that's why it's called
"default". If something is implicit, I'd prefer something that is as
predictable as possible. If something doesn't have context, it's either
going to get an error or it's going to use the global services. neither
of these possibilities is appealing to me. OTOH, if it does have context,
it's going to use a local service that might also not be the one I want.

> 
>>>>I suggest that it is often not the code that looks
>>>>up the views that cares. The system designer cares.
>>>
>>>Yes, as a system designer I care that I don't have to satisfy a lot
>>>of criteria in my context just in order to use an object somewhere
>>>else. In my mind, that object has its own context.
>>
>>So you and I discussed this in IRC.  So we have the hypothetical situation
>>that object C in S1 wants to use A. It wants to use A in such a way that
>>the default place that A will look for components is in S11.  I don't
>>have a good answer for this.  I have to admit that I'm somewhat skeptical
>>that this is a situation that will occur much, if at all, in practice.
>>I'm open to suggestions.
> 
> 
> It seems to me that whenever code manipulates objects in another container

You mean "site", not container.  A site defines configuration.  All of the
objects in a site use the same configurations and components.

> at all there's a chance of this happening, and I, the writer of calling the 
> code, have no indication that this *cannot* happen. There may be a
> site management folder placed there by someone and this can now cause
> my code to break. Or if there's an existing one, any chance to it
> can cause my code to break as I haven't supplied the right components.
>
> I seem to have no way to avoid this; the only case where this does not happen
> if the other person has made sure to use the context=self pattern described
> above in all component lookups; I'm not sure this is even possible as
> some framework code may be doing this too..  At least in that case
> it'll presumably work with a bunch of well known interfaces, though I'm
> still concerned there may be a kind of 'spreading' effect as soon as one
> object (implementing a well known interface) is put in the mix that does
> component lookups of some less-common interface.

A component designer can specify and then must rely on it's component
dependencies. (At some point, we need to come up with this specification
scheme.)  It is the site assembler's job to make sure that the dependcies are
met. If I get a bunch of electronic components and assemble them incorrectly,
the components will not perform their functions correctly. This isn't the
fault of the component designers.

I suggest that whenever an application shares components accross sites, it's
the responsibility of the application assembler to make sure than the shared
components have a supportive environment whereever they are used. This is a
burden on the application assembler that is, hopefully, justified by the benefits.
If it isn't than such sharing should be avoided.

I suggest that the most common case of sharing is the case of nested sites
where the sub-site overrides configuration of the containing sites, An
object from the super-site will use customized components when used in the
subsite.


> 
>>>>>What if I have two interfaces that do some storage, one using a 
>>>>>relational backend and the other the ZODB? Exact same interfaces but I 
>>>>>certainly don't
>>>>>want to switch from one to the other just like that.
>>>>
>>>>Why? What difference does it make. The client of those interfaces 
>>>>shouldn't
>>>>care.  If the client's do care, there's a problem with the interfaces.
>>>
>>>Does that mean storage needs to be explicit in the interface?
>>
>>Only if the client cares about the storage.  It isn't not in the interface,
>>the client probably doesn't care. If it does care, the client should
>>be using a different interface.
> 
> 
> The client doesn't care about storage, it just assumes it's using
> the *same* storage as another client.

In this case, the client cares very much about the storage. Note that
it doesn't care about the type of storage at all. ZODB or RDBMS make
no difference. The cleint wants to use the same storage instance.
If the client really wants to maintain that invariant, it should really
hold a refernce to the storage.


 > If it has to supply it in the
> context itself, it can never be sure.

I'm not esure what all of these "it"s are, but I'll note that if a
client has a contract that it always uses the storage for its site,
then it can satisfy this by being context aware and providing itself as
the context for component lookup.


> 
>>>If I'm telling
>>>
>>>somewhere.fridge.store('cheese')
>>>
>>
>>>from my context, where I have a utility that implements IFridgeStorage
>>
>>>using a ZODB backend, and then I go do this from some other context where
>>>the utility is supplied that uses a relational backend, I'll get
>>>confused, right?
>>
>>Why would you get confused?  My family used to kave an old Kerosene 
>>refrigerator.
>>No I have an electric one. I didn't have any problem with thw switch. The 
>>interface
>>was the same. They both had doors and the stuff inside stayed cold.
> 
> 
> Well, if you had the kerosene fridge in your context, and your wife has
> the electric one in her context, and you're putting stuff in and she
> can't find them, this is rather odd, innit? :)

As noted above, this has nothing to do with storage type.  If I always
want to use the fridge in my house, then I need to pass the house
to the fridge lookup routine. This is easy enough to do.  If I always
want to use the same fridge no matter what, then I'd better keep a reference
to it.

> [snip]
> 
>>>Perhaps I'm just completely missing the point, but I see difficulty
>>>both with this, and with the requirement that I suddenly need to 
>>>supply a lot of components that my site doesn't directly deal with,
>>>just in order to use a different area of the system. 
>>
>>Sometimes, it just doesn't pay to try to do this.  It may be, that I'll be
>>unable to use an object from another site if that object:
>>
>>- Has things that it expects to have met by the using environment, and
>>
>>- I can't meet those needs.
>>
>>Sometimes, you just can't keep a wild animal as a pet. :)
> 
> 
> If it were supplying its own environment I would not have to meet its
> needs in order to use it.

This corresponds to the first case above.  In the proposal, an object
certainly has the option of providing it's own environment.

 > I have to supply its environment now, and
> this causes problems in that I may not know what to supply at all.
> Someone else may be supplying a different environment. If i use a
> registry in one location it may be tucking away its data depending on
> my context, and that means someone else using the same registry with
> a different context in hand may never be able to find it again.
> They can't control what context I am using, and I can't control
> what context they are using. If the registry has its own context,
> this is not a concern.

Frankly, this is a use case that doesn't seem very important to me.

You seem to want an object to *always* use it's own environment. This is
unaacceptable to me because:

- Many object's don't keep track of their environment

- In most cases, I want components to adapt to their envoronment by
   using the components provided their. I'm willing to place the burden
   on the application assemler to make sure that the necessary components
   are in place to support cross-site sharing if that's something the
   application wants to do.

   I acknowledge that there *are* cases where an object must use components
   from it's site. When such constraints exist, there is an explicit way
   to satisfy them.

...


>>>I mean, naively thinking I think it should
>>>be possible to use (call a method on) an object without having to 
>>>worry about how this object interacts with other objects. I'd call
>>>this use.
>>
>>Right, but somebody has to worry about it. Whoever introduced that component
>>into the system should have arranged for the other components it needed.
> 
> 
> But I thought that exactly this cannot really be done in this case.
> If the context=self pattern is used a lot it might be done, but 
> if you're just *assembling* components you have no control over this.
> And if it's not used, the component assembler cannot decide for
> instance that a certain service uses a RDB backend by plugging in the
> right component for this.

In the example you have, the type of storage doesn't matter.  The invariant
you have way that an object always used the same storage, regardless of type.
This is a very specific requiremnt that should be met by establishing a direct
association between the components or by writing the component to always
use the storage from it's site.


 > Unless the component assembler has complete
> control over the whole system,

They generally do.

 > that is, but presumably we have different
> sites in a system in part for the reason that there may be multiple
> independent component assemblers (still working together).

If you truly have independent assemblers, then sharing content accross
systems is going to depend on some coordination. If adequate coordination
isn't possible, then sharing is not a good idea.

> 
>>>Hm, I'm reasoning from the case where I, the author of components, also 
>>>have
>>>an idea of how they're assembled. In fact, I may be actually writing
>>>an application, worrying about assemblages and so on.
>>>
>>>I am also assuming that some other area of the application is created
>>>and assembled by someone else. Now I want to interact with this other
>>>area. There are multiple author/assemblers. 
>>>
>>>>From the pure assembler point of view.. Imagine I have this assemblage
>>>of components. Now I want to hook up one component to another component
>>>which is in someone else's assemblage. In your model my context matters, so
>>>this would mean that I would need to replicate a version of this other
>>>assemblage in my own assembly before it all works. Is that a correct
>>>interpretation?
>>
>>I suppose so. But if you are using the other assemblage, doesn't it already
>>have one it needs for it's own requirements?
> 
> 
> I don't understand here what you are referring to by saying 'doesn't it
> already have one'? One context? I thought was is not possible for this
> other assemblage to use its own context when I call into it from
> another site, at least not without altering its code.

If I were hooking component assemblages together, I would hook them together
in the same site.  In this case, everything in the original assemblages is
in the same site and this available.


> 
>>You'd be typically building a
>>single site by composing these and other assemblages.
> 
> 
> I'm not sure what this means. Where are these assemblages coming from?
> We have one site, so one assemblage?

No, a site can be built from multiple assemblages.



> 
>>>Imagine there's a site X, which contains objects. It somehow has a need
>>>for a count of those objects, and looks up a service which maintains this
>>>count.
>>>
>>>Now I have a site Y, which uses facilities in site X by calling into
>>>it. I have to supply a version of the counting service in site X. But
>>>this version of the service won't have the correct count.
>>
>>So you are accessing some facility that depends on the count.  So, let's
>>say the job of this facility is to generate a count report.  There are
>>2 possibilities:
>>
>>1. The facility generates a count report for the place it's used.
>>   In that case, it will report Y's count and it's doing it's job.
> 
> 
> X needs the count, not Y, and X will get screwed up if the facility reports
> Y's count. Y is not using the count at all directly. Y's just using X.

So case 1 doesn't apply.

>
>>2. The facility generates an "X count report". That is, it's job is
>>   to report specifically on the count in X. Well, in that case,
>>   the facility had better supply a specific context when it gets
>>   the count service so that it gets the right one.
> 
> 
> Can't this lead to lots of confusion and misunderstanding? 

I don't see why if you're clear about the specification. If a component
is truly site-specific, then that should be part of it's specification
and it should supply it's own context.

 > It seems to
> me such a data dependency is fairly easily introduced  and supplying
> the context to be self is easily forgotten. When testing the thing
> even works. It's just that when you reuse X from somewhere else that
> it starts failing.

Perhaps this is a danger.

OTOH, the issues that I've raised need solutions:

- Not all objects track context.  When these objects access the component
   architecture, the results they get should reflect site-specific
   customizations.

- Sometimes objects will be used in a site that is related to but different
   than the site they live in. In most cases, the components used with these objects
   should be chosen from the host site.  This seems obvious in the case of
   presentation components. I don't think it usually applies to non-presentation
   components as well.

Jim

-- 
Jim Fulton           mailto:jim at zope.com       Python Powered!
CTO                  (703) 361-1714            http://www.python.org
Zope Corporation     http://www.zope.com       http://www.zope.org




More information about the Zope3-dev mailing list