I'm not sure I grok what rightness has to do about it. I think this is right, to me wrong == broken. This is not broken.
Let me persuade you.
* someone adds an property named feed to an object at an intermediate location in the containment heirarchy. This breaks the cron job that calls self.Zoo.Diet.LargeAnimals.hippo.feed(), and all the hippos starve.
This is the classic anti-acquisition argument, but it's a red herring.
I used to believe that too, but no longer agree. I changed my mind after developing a large application in Zope, and spending alot of time firefighting the problems that it caused.
The same argument applies to inheritance; introducing an attribute between two classes in a generalized relationship and your app breaks and all the hippos starve anyway.
This analogy is false. If a programmer is responsible for a class and it becomes broken in that way then yes, he is at fault. Fortunately there are well understood principles for design inheritance relationships to keep this easy. Each project has a finite number of classes. Each class has dependencies to only a small number of other classes. Testing (is used appropriately) can be used to ensure correctness, and this probably means re-testing each derived class when a base class changes. The same is not true of a containment heirarchy. The containment heirarchy is managed by content managers, who are responsible for content. After adding content they might test that content, but they are unlikely to retest any functionality - its not their responsibility. The containment heirarchy is often large and sprawling. Acquisition-based bugs occur on a per-instance basis, not per-class, and typically there will be very many more instances in a system than there are classes. After a change to an instance there is a need to re-test *every* *instance* below the change in the containment heirachy. (When was the last time you changed your root folder? Did you test your whole site?). This makes it impractical to test them all. You raised the question of whether this is an anti-acquisition argument or a containtment-vs-context-binding argument. Please dont misunderstand me; acquisition is great when used appropriately. However if methods bound to containement then acquisition could not be used for the purpose you are demonstrating here.
Zope cannot be robust against programmer error. Nothing can.
I, as a programmer, develop Zope products. My users install them on their system. If your users are programmers then this comment is relevant, but I dont. Adding a property to an object (using the Property tab in the management interface) is a user-level operation. I do expect my systems to be robust against user error. (Note that it is even possible to 'break' Zope's own management interface by adding some carefully named properties. Some of those are even WikiNames ;-)
* someone uses self.Zoo.Diet.buildings.visitor_reception.feed(), and ends up filling the reception with hippo food. (This might even be possible for someone who has no permissions on the reception object)
This is once again programmer error.
Do you mean the programmer who implemented 'feed'? If yes, Im pleased you agree with me. Their mistake was to use acquisition instead of inheritance. If they wanted to use acquistion then they would need to augment their otherwise simple implementation of 'feed' with either: 1. Explicit security checks (hard to get right) 2. Explicit is_instance checks (inflexible) 3. Accept the fact that anyone granted the 'Feed Hippos' permission on any hippo may dump hippo feed anywhere, or feed other hippos for which they do not have that permission. Perhaps the hippo analogy isnt helping, so heres a more concrete example. In zope today it is possible for a user who has been granted the 'View Management Screens' permission in *one* folder to create a one-line dtml method that lets him see the management page of any other dtml method in the whole site. Why? because DTMLMethod's manage_main binds to context not containment.
* someone uses
self.Zoo.buildings.office.printers.laserjet1.Zoo.Diet.LargeAni mals.hippo.feed(),
and ends up feeding paper to the hippo. (that could even be someone who has no other permisions on that hippo object)
This is the same as your first two arguments: programmer error.
(Note: Michel is right that this one is not immediately relevant to the binding discussion; its purely a demonstration of a misuse of acquisition. Its also a programmer error, but in Michel is that programmer) The real problem here is that you are relying on acquisition from a context that is not a direct container. Each instance only has one containment heirarchy. However it has an infinite number of possible contexts, which are chosen by the caller. Suppose you have an (apparently correct) external method in Zoo: def feeding_time(self): self.Diet.LargeAnimals.hippo.feed() A malicious user browses to /Zoo/buildings/office/laserjet1/feeding_time. The problem is that acquisition from context outside the containment heirarchy doesnt do what you expect. When the external method looks for the 'feed' attribute it will actually find it from the printer object. This allows a malicious user to call methods on the printer object to which he does not have permission. Jim (if you read this far): Was there a good reason why acquisition needed to look anywhere other than containment heirarchy?
Ive now nearly finished converting all my newbie zope projects back to a conventional O-O design. I have been bitten by all the problems listed above. The feed method *should* *be* implemented in a ZooAnimal base class.
Ok, that's a valid approach.
Can you explain any advantage of the approach you are advocating?
Toby Dickenson wrote:
Sorry for the delay (book ... crushing ... head ... )
Perhaps the hippo analogy isnt helping, so heres a more concrete example. In zope today it is possible for a user who has been granted the 'View Management Screens' permission in *one* folder to create a one-line dtml method that lets him see the management page of any other dtml method in the whole site.
Even above where the user is defined? Are you sure? That would be a bug. Your arguments are all pretty solid, I think we are just looking at things from different perspectives.
Ive now nearly finished converting all my newbie zope projects back to a conventional O-O design. I have been bitten by all the problems listed above. The feed method *should* *be* implemented in a ZooAnimal base class.
Ok, that's a valid approach.
Can you explain any advantage of the approach you are advocating?
We're working to make Zope usable from the perspective of many different, possibly non-programming users. I agree that a feed method on an animal base class is a good way to do it from the perspective of an python OO programmer (which you and I are), but people need to be able to pick up, say, our book, and start creating useful dynamic content by dropping in objects, scripts, HTML and other stuff they can grok. I agree that this can also be a hinderance, I did some training in california a few months ago and the company was doing all of their application in DTML and external Methods and I convinced them (rather easily because they were all coders) to make a Python product instead. Zope's audience can be pictured, like many things, as a bell curve. We're (most of us at least) all sittin' up on the top of the hump looking down. Understanding the perspective of someone at the bottom looking up is essential to making sure Zope succeeds. Python Methods are a perfect example of an object that is useful to us at the top for advanced things, like methods of a animal base class we use in our complex Zoo application, but they are also just as useful to those at the bottom, as a simple little script written in python that feeds the hippos. Or unobfusicates their DTML. Or iterates over a database query. It's useful and saves the time of these novice Zope users if these methods can be flexible and reusable in different contexts. To me, this is the heart of the container vs. context binding argument. We at the top ususaly want the container because we're engineering methods on classes. Those at the bottom usually want context for entirely different reasons. Evan has done a great job making an object that satisfies the entire spectrum, but in the process has come up with something that is not entirely unlike a method in python. I'm not longer arguing or advocating a particular stance on container vs. context, Evan talked me into the legitimacy of both approaches. Right now, I'm arguing that we need a name that makes sense to everybody. 'Method' makes sense to us at the top, but not to those on the bottom, and there are a lot more of those people than us. -Michel
Michel Pelletier wrote:
Python Methods are a perfect example of an object that is useful to us at the top for advanced things, like methods of a animal base class we use in our complex Zoo application, but they are also just as useful to those at the bottom, as a simple little script written in python that feeds the hippos. Or unobfusicates their DTML. Or iterates over a database query. It's useful and saves the time of these novice Zope users if these methods can be flexible and reusable in different contexts. To me, this is the heart of the container vs. context binding argument. We at the top ususaly want the container because we're engineering methods on classes. Those at the bottom usually want context for entirely different reasons. Evan has done a great job making an object that satisfies the entire spectrum, but in the process has come up with something that is not entirely unlike a method in python.
...this clears things up a lot. So, a python x can be bound to containment _or_ context depending on how it's setup, right? Okay, if that's the case, then we need to make sure that the situations where you use context binding and the situations where you use containment binding are clearly explained. If Zope is to succeed really well, we should make sure that people at the bottom of the bell-curve don't end up feeding paper to hippos or filling receptions with hippo feed ;-) It's bad if someone rises up the bell curve and then realises all their context-bound methods could be badly abused, they're left feeling stupid and with the impression it's easy to create serious problems with Zope. By the time they've first learn to use python x's, they should know when to use context and when to use containment.
I'm arguing that we need a name that makes sense to everybody. 'Method' makes sense to us at the top, but not to those on the bottom, and there are a lot more of those people than us.
I say leave it to the vote, although I liked that complex voting algorithm thing someone posted, it sounds like it'd leave more people happier. cheers, Chris
Chris Withers wrote:
Michel Pelletier wrote:
Python Methods are a perfect example of an object that is useful to us at the top for advanced things, like methods of a animal base class we use in our complex Zoo application, but they are also just as useful to those at the bottom, as a simple little script written in python that feeds the hippos. Or unobfusicates their DTML. Or iterates over a database query. It's useful and saves the time of these novice Zope users if these methods can be flexible and reusable in different contexts. To me, this is the heart of the container vs. context binding argument. We at the top ususaly want the container because we're engineering methods on classes. Those at the bottom usually want context for entirely different reasons. Evan has done a great job making an object that satisfies the entire spectrum, but in the process has come up with something that is not entirely unlike a method in python.
...this clears things up a lot. So, a python x can be bound to containment _or_ context depending on how it's setup, right?
Sorta. There is a bindings tab on PythonMethods where you assign names to various variables that show up in your namespace. 'self' is the container. 'context' is the context. So: self.objectValues() will allways call the container whereas: context.objectValues() will call the method on the currently bound object.
I say leave it to the vote, although I liked that complex voting algorithm thing someone posted, it sounds like it'd leave more people happier.
Unfortunately, complex also means "too hard for me to think too hard about". Any volunteers? -Michel
Michel Pelletier wrote:
Sorta. There is a bindings tab on PythonMethods where you assign names to various variables that show up in your namespace. 'self' is the container. 'context' is the context. So:
self.objectValues()
will allways call the container whereas:
context.objectValues()
will call the method on the currently bound object.
...but can you bind self to the context or, more perversely, context to the container?
Unfortunately, complex also means "too hard for me to think too hard about". Any volunteers?
How about the person who suggested it? ;-) cheers, Chris
Michel Pelletier wrote:
Chris Withers wrote:
Michel Pelletier wrote:
Python Methods are a perfect example of an object that is useful to us at the top for advanced things, like methods of a animal base class we use in our complex Zoo application, but they are also just as useful to those at the bottom, as a simple little script written in python that feeds the hippos. Or unobfusicates their DTML. Or iterates over a database query. It's useful and saves the time of these novice Zope users if these methods can be flexible and reusable in different contexts. To me, this is the heart of the container vs. context binding argument. We at the top ususaly want the container because we're engineering methods on classes. Those at the bottom usually want context for entirely different reasons. Evan has done a great job making an object that satisfies the entire spectrum, but in the process has come up with something that is not entirely unlike a method in python.
...this clears things up a lot. So, a python x can be bound to containment _or_ context depending on how it's setup, right?
Sorta. There is a bindings tab on PythonMethods where you assign names to various variables that show up in your namespace. 'self' is the container. 'context' is the context. So:
self.objectValues()
will allways call the container whereas:
context.objectValues()
will call the method on the currently bound object.
I say leave it to the vote, although I liked that complex voting algorithm thing someone posted, it sounds like it'd leave more people happier.
Unfortunately, complex also means "too hard for me to think too hard about". Any volunteers?
That would be me. Sure I'll do it. Just set up the voting page so people can rank their choices, and give me the data afterwards and I'll do the tallying. Michael.
participants (4)
-
Chris Withers -
Michael Bernstein -
Michel Pelletier -
Toby Dickenson