RE: [Zope-dev] Michel's Reply
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()
[Zope-dev] Michel's Reply
From: Michel Pelletier [EMAIL PROTECTED] Jim Fulton wrote: Michel, You have advocated that methods should always be bound to the objects they are accessed in. You argue that there should be no choice in the matter. I advocate more points than that, like being able to document python with python, no XML mixed in with a language, Py Meths working like other methods do, but yes that is one of them. My argument should be more flexible in regard to choice, a willing compromize is to switch the default binding of context and container, making 'self' the default for context and something else the default for container. I have to disagree strongly. I'll try to explain why. In Python, methods are bound to instances. Methods are part of an instance's core behavior. They are specific to the kind of thing the instance is. In my words, methods are part of the genetic makeup of an object. Python methods are meant for through the web usability and programming ala the existing Zope model. 90% of your audience just scratched their heads. In Zope, we allow some methods to be bound to their context. This is done in a number of ways and is sometimes very useful. We have methods, like standard_html_header, which are designed to be used in different contexts. this is how I feel python methods should be designed to be used. We have other methods, like manage_edit that are designed to work on specific instances. It would be an egregious error if this method was acquired and applied to it's context. I think this is a weak argument, none of the built in Zope methods mean anything to the average user, they can't find them, click on them, edit them, or copy them to their own method to change and experiment with. We have some methods that are designed to bound to an instance (container, in your terminology) Python Method terminology but that, because they are written in DTML, can be bound to other objects. This can cause significant problems. For example, methods defined in ZClasses almost always want to be bound to ZClass instances, not to other arbitrary objects. asideThere's a bonus problem with DTML Methods. When a DTML Method is invoked from another DTML Method, it is bound to neither the object it was accessed in or to the object it came from. It is bound to the calling namespace. It turns out that this is a useful behavior if the DTML Method is designed to be used as a "subtemplate". /aside This is because DTML binding is implicit, not because it's backward. This problem doesn't effect python methods because you allways bind a method in python when you call it. The question is how the initial method gets bound. There is no one "right" way to bind a method. There are good reasons to sometimes bind a method to it's context and sometimes bind a method to it's container (ie instance). There are even sometimes reasons to bind a method to a calling namespace. The principle of least surprise doesn't help here, because methods defined in Python classes don't behave the way methods defined through the web do currently. We *need* control over binding, as well as reasonable defaults. If we can agree that we need binding control, the question arises as to some details and default names. I agree there must be control over binding, although your arguments above have not convinced me that Python Methods are doing it the right way, actually, it's convinced more than my argument, compromisingly of course, is more right. Should it be possible to do more than one binding at a time, using multiple names? If not, then I'd agree that the name 'self' should be used for either the context or container binding. I'm with you so far with that. If both bindings are allowed at the same time, then 'self' should refer to container binding to be consistent with standard Python usage and some name like 'context' should be used (by default) for contextual binding. That's what I disagree with. I don't think you've given a strong corollary to standard python usage. The only strong argument you've given so far is the ZClass one, but 10 gives you 1 that's not the primary use case (it may be the one in Fburg). People are going to be defining these methods in Zope to make their lives easier, probably bad design and mad hacks and no structure, but that's how 90% of the world gets their work done and easing that burden is the usability task. You have not convinced me that: 1. Something called a Python Method should not resemble a method definition in python. 2. Something called a Method in Zope should not behave like the other Methods (meaning through the web objects) in Zope 3. Common, *documented* well understood URL manipulations (context) are less important than (the less common) containment oriented design and ZClasses. Consider the following passage in the documentation: For example suppose you want to call a method