RE: [Zope-dev] Michel's Reply

2000-10-23 Thread Toby Dickenson

 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

2000-10-20 Thread Chris Withers


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