~sidnei/zope3/reduce-deps

« back to all changes in this revision

Viewing changes to src/zope/structuredtext/regressions/Acquisition.stx

  • Committer: Sidnei da Silva
  • Date: 2010-07-05 21:07:01 UTC
  • Revision ID: sidnei.da.silva@canonical.com-20100705210701-zmqhqrbzad1mhzsl
- Reduce deps

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
Acquisition
2
 
 
3
 
  "Copyright (C) 1996-1998, Digital Creations":COPYRIGHT.html.
4
 
 
5
 
  Acquisition [1] is a mechanism that allows objects to obtain
6
 
  attributes from their environment.  It is similar to inheritence,
7
 
  except that, rather than traversing an inheritence hierarchy
8
 
  to obtain attributes, a containment hierarchy is traversed.
9
 
 
10
 
  The "ExtensionClass":ExtensionClass.html. release includes mix-in
11
 
  extension base classes that can be used to add acquisition as a
12
 
  feature to extension subclasses.  These mix-in classes use the
13
 
  context-wrapping feature of ExtensionClasses to implement
14
 
  acquisition. Consider the following example::
15
 
 
16
 
    import ExtensionClass, Acquisition
17
 
 
18
 
    class C(ExtensionClass.Base):
19
 
      color='red'
20
 
 
21
 
    class A(Acquisition.Implicit):
22
 
 
23
 
      def report(self):
24
 
        print self.color
25
 
 
26
 
    a=A()
27
 
    c=C()
28
 
    c.a=A()
29
 
 
30
 
    c.a.report() # prints 'red'
31
 
 
32
 
    d=C()
33
 
    d.color='green'
34
 
    d.a=a
35
 
 
36
 
    d.a.report() # prints 'green'
37
 
 
38
 
    a.report() # raises an attribute error
39
 
 
40
 
  The class 'A' inherits acquisition behavior from
41
 
  'Acquisition.Implicit'.  The object, 'a', "has" the color of
42
 
  objects 'c' and 'd' when it is accessed through them, but it
43
 
  has no color by itself.  The object 'a' obtains attributes
44
 
  from it's environment, where it's environment is defined by
45
 
  the access path used to reach 'a'.
46
 
 
47
 
  Acquisition wrappers
48
 
 
49
 
    When an object that supports acquisition is accessed through
50
 
    an extension class instance, a special object, called an
51
 
    acquisition wrapper, is returned.  In the example above, the
52
 
    expression 'c.a' returns an acquisition wrapper that
53
 
    contains references to both 'c' and 'a'.  It is this wrapper
54
 
    that performs attribute lookup in 'c' when an attribute
55
 
    cannot be found in 'a'.
56
 
 
57
 
    Aquisition wrappers provide access to the wrapped objects
58
 
    through the attributes 'aq_parent', 'aq_self', 'aq_base'.  
59
 
    In the example above, the expressions::
60
 
      
61
 
       'c.a.aq_parent is c'
62
 
 
63
 
    and::
64
 
 
65
 
       'c.a.aq_self is a'
66
 
 
67
 
    both evaluate to true, but the expression::
68
 
 
69
 
       'c.a is a'
70
 
 
71
 
    evaluates to false, because the expression 'c.a' evaluates
72
 
    to an acquisition wrapper around 'c' and 'a', not 'a' itself.
73
 
 
74
 
    The attribute 'aq_base' is similar to 'aq_self'.  Wrappers may be
75
 
    nested and 'aq_self' may be a wrapped object.  The 'aq_base'
76
 
    attribute is the underlying object with all wrappers removed.
77
 
 
78
 
  Acquisition Control
79
 
 
80
 
    Two styles of acquisition are supported in the current
81
 
    ExtensionClass release, implicit and explicit aquisition.
82
 
  
83
 
    Implicit acquisition
84
 
    
85
 
      Implicit acquisition is so named because it searches for
86
 
      attributes from the environment automatically whenever an
87
 
      attribute cannot be obtained directly from an object or
88
 
      through inheritence.
89
 
  
90
 
      An attribute may be implicitly acquired if it's name does
91
 
      not begin with an underscore, '_'.
92
 
  
93
 
      To support implicit acquisition, an object should inherit
94
 
      from the mix-in class 'Acquisition.Implicit'.
95
 
  
96
 
    Explicit Acquisition
97
 
  
98
 
      When explicit acquisition is used, attributes are not
99
 
      automatically obtained from the environment.  Instead, the
100
 
      method 'aq_aquire' must be used, as in::
101
 
  
102
 
        print c.a.aq_acquire('color')
103
 
  
104
 
      To support explicit acquisition, an object should inherit
105
 
      from the mix-in class 'Acquisition.Explicit'.
106
 
 
107
 
    Controlled Acquisition
108
 
 
109
 
      A class (or instance) can provide attribute by attribute control
110
 
      over acquisition.  This is done by:
111
 
 
112
 
      - subclassing from 'Acquisition.Explicit', and
113
 
 
114
 
      - setting all attributes that should be acquired to the special
115
 
        value: 'Acquisition.Acquired'.  Setting an attribute to this
116
 
        value also allows inherited attributes to be overridden with
117
 
        acquired ones.
118
 
 
119
 
        For example, in::
120
 
 
121
 
          class C(Acquisition.Explicit):
122
 
             id=1
123
 
             secret=2
124
 
             color=Acquisition.Acquired
125
 
             __roles__=Acquisition.Acquired
126
 
 
127
 
        The *only* attributes that are automatically acquired from
128
 
        containing objects are 'color', and '__roles__'.  Note also
129
 
        that the '__roles__' attribute is acquired even though it's
130
 
        name begins with an underscore.  In fact, the special
131
 
        'Acquisition.Acquired' value can be used in
132
 
        'Acquisition.Implicit' objects to implicitly acquire selected
133
 
        objects that smell like private objects.
134
 
 
135
 
    Filtered Acquisition
136
 
 
137
 
      The acquisition method, 'aq_acquire', accepts two optional
138
 
      arguments. The first of the additional arguments is a
139
 
      "filtering" function that is used when considering whether to
140
 
      acquire an object.  The second of the additional arguments is an
141
 
      object that is passed as extra data when calling the filtering
142
 
      function and which defaults to 'None'.
143
 
 
144
 
      The filter function is called with five arguments:
145
 
 
146
 
      - The object that the 'aq_acquire' method was called on,
147
 
 
148
 
      - The object where an object was found,
149
 
 
150
 
      - The name of the object, as passed to 'aq_acquire',
151
 
 
152
 
      - The object found, and
153
 
 
154
 
      - The extra data passed to 'aq_acquire'.
155
 
 
156
 
      If the filter returns a true object that the object found is
157
 
      returned, otherwise, the acquisition search continues.
158
 
 
159
 
      For example, in::
160
 
 
161
 
        from Acquisition import Explicit
162
 
        
163
 
        class HandyForTesting:
164
 
            def __init__(self, name): self.name=name
165
 
            def __str__(self):
166
 
                return "%s(%s)" % (self.name, self.__class__.__name__)
167
 
            __repr__=__str__
168
 
        
169
 
        class E(Explicit, HandyForTesting): pass
170
 
        
171
 
        class Nice(HandyForTesting):
172
 
            isNice=1
173
 
            def __str__(self):
174
 
                return HandyForTesting.__str__(self)+' and I am nice!'
175
 
            __repr__=__str__
176
 
        
177
 
        a=E('a')
178
 
        a.b=E('b')
179
 
        a.b.c=E('c')
180
 
        a.p=Nice('spam')
181
 
        a.b.p=E('p')
182
 
        
183
 
        def find_nice(self, ancestor, name, object, extra):
184
 
            return hasattr(object,'isNice') and object.isNice
185
 
        
186
 
        print a.b.c.aq_acquire('p', find_nice)
187
 
 
188
 
      The filtered acquisition in the last line skips over the first
189
 
      attribute it finds with the name 'p', because the attribute
190
 
      doesn't satisfy the condition given in the filter. The output of
191
 
      the last line is::
192
 
 
193
 
        spam(Nice) and I am nice!
194
 
 
195
 
  Acquisition and methods
196
 
 
197
 
    Python methods of objects that support acquisition can use
198
 
    acquired attributes as in the 'report' method of the first example
199
 
    above.  When a Python method is called on an object that is
200
 
    wrapped by an acquisition wrapper, the wrapper is passed to the
201
 
    method as the first argument.  This rule also applies to
202
 
    user-defined method types and to C methods defined in pure mix-in
203
 
    classes.
204
 
 
205
 
    Unfortunately, C methods defined in extension base classes that
206
 
    define their own data structures, cannot use aquired attributes at
207
 
    this time.  This is because wrapper objects do not conform to the
208
 
    data structures expected by these methods.
209
 
 
210
 
  Acquiring Acquiring objects
211
 
 
212
 
    Consider the following example::
213
 
 
214
 
      from Acquisition import Implicit
215
 
      
216
 
      class C(Implicit):
217
 
          def __init__(self, name): self.name=name
218
 
          def __str__(self):
219
 
              return "%s(%s)" % (self.name, self.__class__.__name__)
220
 
          __repr__=__str__
221
 
      
222
 
      a=C("a")
223
 
      a.b=C("b")
224
 
      a.b.pref="spam"
225
 
      a.b.c=C("c")
226
 
      a.b.c.color="red"
227
 
      a.b.c.pref="eggs"
228
 
      a.x=C("x")
229
 
 
230
 
      o=a.b.c.x
231
 
 
232
 
    The expression 'o.color' might be expected to return '"red"'. In
233
 
    earlier versions of ExtensionClass, however, this expression
234
 
    failed.  Acquired acquiring objects did not acquire from the
235
 
    environment they were accessed in, because objects were only
236
 
    wrapped when they were first found, and were not rewrapped as they
237
 
    were passed down the acquisition tree.
238
 
 
239
 
    In the current release of ExtensionClass, the expression "o.color"
240
 
    does indeed return '"red"'.
241
 
 
242
 
    When searching for an attribute in 'o', objects are searched in
243
 
    the order 'x', 'a', 'b', 'c'. So, for example, the expression,
244
 
    'o.pref' returns '"spam"', not '"eggs"'.  In earlier releases of
245
 
    ExtensionClass, the attempt to get the 'pref' attribute from 'o'
246
 
    would have failed.
247
 
 
248
 
    If desired, the current rules for looking up attributes in complex
249
 
    expressions can best be understood through repeated application of
250
 
    the '__of__' method:
251
 
 
252
 
    'a.x' -- 'x.__of__(a)'
253
 
 
254
 
    'a.b' -- 'b.__of__(a)'
255
 
 
256
 
    'a.b.x' -- 'x.__of__(a).__of__(b.__of__(a))'
257
 
 
258
 
    'a.b.c' -- 'c.__of__(b.__of__(a))'
259
 
 
260
 
    'a.b.c.x' --
261
 
        'x.__of__(a).__of__(b.__of__(a)).__of__(c.__of__(b.__of__(a)))'
262
 
 
263
 
    and by keeping in mind that attribute lookup in a wrapper
264
 
    is done by trying to lookup the attribute in the wrapped object
265
 
    first and then in the parent object.  In the expressions above
266
 
    involving the '__of__' method, lookup proceeds from left to right.
267
 
 
268
 
    Note that heuristics are used to avoid most of the repeated
269
 
    lookups. For example, in the expression: 'a.b.c.x.foo', the object
270
 
    'a' is searched no more than once, even though it is wrapped three
271
 
    times.
272
 
 
273
 
.. [1] Gil, J., Lorenz, D., 
274
 
   "Environmental Acquisition--A New Inheritance-Like Abstraction Mechanism",
275
 
   http://www.bell-labs.com/people/cope/oopsla/Oopsla96TechnicalProgramAbstracts.html#GilLorenz, 
276
 
   OOPSLA '96 Proceedings, ACM SIG-PLAN, October, 1996
277