[Zope3-checkins] CVS: Zope3/src/zope/app - context.txt:1.3

Jim Fulton jim@zope.com
Fri, 6 Jun 2003 10:42:26 -0400


Update of /cvs-repository/Zope3/src/zope/app
In directory cvs.zope.org:/tmp/cvs-serv19032

Modified Files:
	context.txt 
Log Message:
Added some tutorial information from Steve's IRC class.

Added some additional notes/musings.


=== Zope3/src/zope/app/context.txt 1.2 => 1.3 ===
--- Zope3/src/zope/app/context.txt:1.2	Fri Jun  6 05:55:00 2003
+++ Zope3/src/zope/app/context.txt	Fri Jun  6 10:42:25 2003
@@ -1,44 +1,266 @@
 Context Decorators
 ==================
 
+Introduction
+------------
+
+Sorry. We should have a general description of context wrappers and
+decorators here, but all we've got are some issues that we're
+wrestling with.
+
+Until we've written a clear description, the following irc dialog is
+instructive:: 
+
+  <SteveA> So... Decorators.
+
+  <SteveA> Before I talk about decorators, I need to spend a little time
+  talking about context wrappers.
+
+  <SteveA> As you know, zope 3 doesn't do implicit acquisition like zope
+  2 does.
+
+  <bigkevmcd> SteveA: is it worth pointing out a source file, to look
+  at...?
+
+  <SteveA> That is, if I have a folder f that contains a document d,
+  f.d.items() will not be equivalent to f.items()
+
+  <SteveA> (assuming that a folder has an items method)
+
+  * faassen listens in. :)
+
+  <mexiKON> SteveA: yeah, i read the wiki page about basic context
+  wrappers
+
+  <SteveA> This is for the better, as it gets rid of the indeterminate
+  nature of implicit acquisition
+
+  <SteveA> however, even though we don't want or need implicit
+  acquisition in zope 3, we still need some way of recording the
+  "context" of a particular journey through the content.
+
+  <mexiKON> right, through context wrappers
+
+  <SteveA> we need this because we want to make policy decisions, and
+  provide different software and settings, at different places
+
+  <mexiKON> like for 'playful' utilities
+
+  <SteveA> for example, we might want to install two different versions
+  of a software package in different parts of a site
+
+  <SteveA> or to customise them differently
+
+  <SteveA> right, this is like local utilities
+
+  <faassen> or two different frontends on websites, a traditional use
+  for acquisition in zope 2
+
+  <SteveA> we've stopped using the terms "placeful" and "placeless" in
+  favour of "local" and "global"
+
+  <SteveA> faassen: actually, that's a bit different
+
+  <faassen> SteveA: wouldn't that be a set of views associated ..um,
+  localfully? :)
+
+  <SteveA> you'd use a different publishing traversal component for
+  that, which would set a different skin to use
+
+  <SteveA> but the context, as far as services etc. are concerned, would
+  be the same
+
+  <faassen> SteveA: hm.
+
+  <SteveA> So, the way we remember the traversal to a particular piece
+  of content is by looking in its context wrapper
+
+  <SteveA> we can get from the wrapper the names used at each step in
+  the traversal, and each of the "parent" objects, all the way down to
+  the root
+
+  <SteveA> you can use what you get from the context wrapper to do the
+  equivalent of implicit acquisition, but do it explicitly
+
+  <SteveA> The way context wrappers work is this:
+
+  <SteveA> >>> o = SomeContentObject()
+
+  <SteveA> >>> wrapped_o = ContextWrapper(o)
+
+  <SteveA> >>> wrapped_o.somemethod() # passes through the
+  call. Equivalent to o.somemethod()
+
+  <SteveA> So, you see that a context wrapper is like a transparent
+  wrapper around the object.
+
+  <SteveA> A decorator is a context wrapper that isn't quite so
+  transparent.
+
+  <mexiKON> what does it not let thru?
+
+  <bigkevmcd> or what does it let through?
+
+  <SteveA> That is, for some methods or attributes, the wrapper itself
+  handles the method call / attribute lookup
+
+  <bigkevmcd> as it's a fixed set
+
+  <SteveA> Take a look at src/zope/app/container/zopecontainer.py
+
+  <SteveA>
+  http://cvs.zope.org/Zope3/src/zope/app/container/zopecontainer.py?rev=HEAD&content-type=text/vnd.viewcvs-markup
+
+  <SteveA> The first class in the file is ZopeContainerAdapter. Don't
+  bother about that.
+
+  <SteveA> About half way down, there is ZopeContainerDecorator.
+
+  <SteveA> You can see that it derives from Wrapper. That is, it is a
+  kind of context wrapper.
+
+  <SteveA> It implements(IZopeContainer). Decorators are set up so that
+  if you say that the class implements some interface, a wrapped object
+  will provide whatever the object provides, plus what the decorator
+  provides.
+
+  <SteveA> Each of the methods in ZopeContainerDecorator are handled by
+  the decorator (that is, by the wrapper), rather than by the object
+  that is wrappe.
+
+  <SteveA> Each of the methods in ZopeContainerDecorator are handled by
+  the decorator (that is, by the wrapper), rather than by the object
+  that is wrapped.
+
+  <SteveA> If you look at the first method, __getitem__, you see that it
+  passes the call to __getitem__ through to the proxied object, but
+  rather than return the retrieved item, it returns the item in a
+  context-wrapper.
+
+  <SteveA> This is helpful when you are working with containers, as, if
+  you have a context-wrapped container, when you get one of its
+  subobjects, it too is context wrapped. So, getPath and getting
+  services for that subobject will work as expected.
+
+  <SteveA> Writing using decorators is another way to keep your content
+  classes looking like ordinary python, and putting zope-specific
+  functionality in a separate place.
+
+  <SteveA> this makes it easier to write tests, easier to see what is
+  going on, easier to use third-party python classes with zope
+
+  <bigkevmcd> by developing the content in python, and then "decorating"
+  it with Zope behaviour via a Decorator?
+
+  <SteveA> right
+
+
 Issues
 ------
 
 * How to decide what permssions to use for new methods introduced in
   the decorator?
 
-Consider a ZopeContainerDecorator. It has a method 'rename' that does not
-appear in IContainer. What permission should guard 'rename'?
-'rename' depends on 'setObject' in IContainer. Different content components
-protect 'setObject' with different permissions; for example,
-zope.ManageContent or zope.ManageServices.
-So, we can't guard 'rename' with one hard-coded permission.
-
-What could we do instead?
-
-- Reorder proxies, decorator outside the security proxy.
-We could somehow re-order the proxies around an object so that the decorator
-goes on the outside. This is awkward, as it breaks our invariant of putting
-the security proxy always on the outside. There are also efficiency issues
-if the interface-related descriptors are security-proxied.
-
-- What protects rename should be what protects setObject.
-We could declare that rename is to be protected with whatever protects the
-'setObject' operation on the proxied object.
-That makes the zcml more complex, and special for decorators.
-That also makes the checker to use for decorators more complex and special.
-
-- Rename gets proxied by magic in the decorator.
-We could declare that rename is a special "untrusted" method, and cause
-its 'self' argument to be bound not to the decorator instance, but to
-some special object that is like the original decorator, but which wraps
-a security-proxied object.
-
-- Register decorators to classes rather than interfaces.
-Security declarations are made by class, not by interface, so it makes sense
-for a decorator that needs particular security declarations to be declared
-for a class, and not an interface.
-It is not possible, currently, to register an adapter for a class.
-If it is made possible to do this, adapters registered for classes would
-always trump those registered for interfaces.
+  Consider a ZopeContainerDecorator. It has a method 'rename' that does not
+  appear in IContainer. What permission should guard 'rename'?
+  'rename' depends on 'setObject' in IContainer. Different content components
+  protect 'setObject' with different permissions; for example,
+  zope.ManageContent or zope.ManageServices.
+  So, we can't guard 'rename' with one hard-coded permission.
+
+  What could we do instead?
+
+  - Reorder proxies, decorator outside the security proxy.
+  We could somehow re-order the proxies around an object so that the decorator
+  goes on the outside. This is awkward, as it breaks our invariant of putting
+  the security proxy always on the outside. There are also efficiency issues
+  if the interface-related descriptors are security-proxied.
+
+  Let's analyze this a bit further.  
+
+  * Why do we have an invariant that security proxies always go on the
+    outside?  I'm not sure I know. I think it may be:
+
+    + Context methods and methods of ContextAware classes get rebound
+    to wrapper. They don't generally expect to be bound to security
+    proxies.  In particular, it's common for context methods to rely
+    on access to private data.
+
+    + Maybe it simplified wrapper manipulation. I *think* recent
+    changes to basic proxy introspection make this a non issue.
+
+  * Adapters commonly are put around proxied objects.
+
+    I see decorators lying along a continuum from totally transparent
+    proxies to adapters.  Given, this, I'm not positive that we have
+    such an invariant.
+
+  * It would be bad for interface descriptors to end up on the wrong
+    side of security proxies.  Security descriptors always need to
+    be bound to objects wo security proxies.
+
+    Note that interface descriptors can deal with context wrappers
+    now, but they would go faster if they didn't need to.
+
+    If we didn't have the ContextAware mix-in, we wouldn't need to
+    worry about effects on interface descriptors, because they
+    wouldn't evey be magically converted to context descriptors.
+
+  * Is there a helpful rule that's less general than "security
+    proxies" always go on the outside?
+
+  - What protects rename should be what protects setObject.
+  We could declare that rename is to be protected with whatever protects the
+  'setObject' operation on the proxied object.
+  That makes the zcml more complex, and special for decorators.
+  That also makes the checker to use for decorators more complex and special.
+
+  - Rename gets proxied by magic in the decorator.
+  We could declare that rename is a special "untrusted" method, and cause
+  its 'self' argument to be bound not to the decorator instance, but to
+  some special object that is like the original decorator, but which wraps
+  a security-proxied object.
+
+  - Register decorators to classes rather than interfaces.
+  Security declarations are made by class, not by interface, so it makes sense
+  for a decorator that needs particular security declarations to be declared
+  for a class, and not an interface.
+  It is not possible, currently, to register an adapter for a class.
+  If it is made possible to do this, adapters registered for classes would
+  always trump those registered for interfaces.
+
+* ContextAware mix-in considered harmful
+
+  ContextAware is a marker mix-in class that causes all descriptors
+  for instances of the class to be implicitly rebound to context
+  wrappers if they are accessed through context wrappers. These
+  descriptors include descriptors not defined in the class statement,
+  such as inherited descriptors and descriptors set by interface
+  declarations. 
+
+  This implicit behavior has a high potential to break the
+  expectations of descrioptors set in the class statement.
+
+  Note that it would not be so evil to have a way to say that all of
+  the descriptors defined in a class statement are context
+  descriptoes, since that is still explicit.  For example::
+
+     class Foo(A, B):
+
+        ContextAware()
+
+        implements(IFoo)
+
+        def m1(self, ...): ...
+
+        def m2(self, ...): ...
+
+        ...
+
+  would assert that m1, m2, etc are context methods but would not
+  affect descriptors provided by A and B. 
+
+  The way to achieve this is with the __metaclass__ advice hack.
 
+  Actually, I *think* the ContextAware mixin could be fixed with a suitably
+  tricky meta class.