~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/canonical/lazr/doc/decorates.txt

  • Committer: Launchpad Patch Queue Manager
  • Date: 2008-12-20 04:58:58 UTC
  • mfrom: (7465.5.6 lazr-config-integration)
  • Revision ID: launchpad@pqm.canonical.com-20081220045858-nctxabte1151hxgz
[r=barry] Integrate lazr.config and lazr.delegates into code

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
= decorates module =
2
 
 
3
 
The decorates module makes it easy to write decorators. A decorator is
4
 
an object which adds some property on to another object, while still
5
 
providing the underlying interface.
6
 
 
7
 
 
8
 
== decorates() ==
9
 
 
10
 
The decorates function makes a class implement an interface by
11
 
delegating the implementation to another object. In the case of a class
12
 
providing an adapter, that object will be the 'context', but it can
13
 
really be any object stored in an attribute. So while the interfaces use
14
 
an inheritance mechanism, the classes use a composition mechanism.
15
 
 
16
 
For example we can define two interfaces IFoo0 <- IFoo...
17
 
 
18
 
    >>> from canonical.lazr import decorates
19
 
    >>> from zope.interface import Interface, Attribute
20
 
    >>> class IFoo0(Interface):
21
 
    ...     spoo = Attribute('attribute in IFoo0')
22
 
 
23
 
    >>> class IFoo(IFoo0):
24
 
    ...     def bar():
25
 
    ...         "some method"
26
 
    ...     baz = Attribute("some attribute")
27
 
 
28
 
And two classes (BaseFoo0 <- BaseFoo) that do something interesting.
29
 
 
30
 
    >>> class BaseFoo0:
31
 
    ...     spoo = 'some spoo'
32
 
 
33
 
    >>> class BaseFoo(BaseFoo0):
34
 
    ...     def bar(self):
35
 
    ...         return 'bar'
36
 
    ...     baz = 'hi baz!'
37
 
 
38
 
SomeClass can implement IFoo by delegating to an instance of BaseFoo
39
 
stored in the 'context' attribute. Note that decorates takes the
40
 
interface as the argument. By default, 'context' is the attribute
41
 
containing the object to which the interface implementation is
42
 
delegated.
43
 
 
44
 
    >>> class SomeClass(object):
45
 
    ...     decorates(IFoo)
46
 
    ...     def __init__(self, context):
47
 
    ...         self.context = context
48
 
 
49
 
    >>> f = BaseFoo()
50
 
    >>> s = SomeClass(f)
51
 
    >>> s.bar()
52
 
    'bar'
53
 
 
54
 
    >>> s.baz
55
 
    'hi baz!'
56
 
 
57
 
    >>> s.spoo
58
 
    'some spoo'
59
 
 
60
 
    >>> IFoo.providedBy(s)
61
 
    True
62
 
 
63
 
The decorates function takes an optional keyword argument to change
64
 
attribute containing the object to delegate to. So an existing class,
65
 
such as SomeOtherClass, can declare the name of the attribute to
66
 
delegate to.
67
 
 
68
 
    >>> class SomeOtherClass(object):
69
 
    ...     decorates(IFoo, context='myfoo')
70
 
    ...     def __init__(self, foo):
71
 
    ...         self.myfoo = foo
72
 
    ...     spoo = 'spoo from SomeOtherClass'
73
 
 
74
 
    >>> f = BaseFoo()
75
 
    >>> s = SomeOtherClass(f)
76
 
    >>> s.bar()
77
 
    'bar'
78
 
 
79
 
    >>> s.baz
80
 
    'hi baz!'
81
 
 
82
 
    >>> s.spoo
83
 
    'spoo from SomeOtherClass'
84
 
 
85
 
    >>> s.baz = 'fish'
86
 
    >>> s.baz
87
 
    'fish'
88
 
 
89
 
    >>> f.baz
90
 
    'fish'
91
 
 
92
 
The decorates() function can only be used in new-style classes. An
93
 
error is raised when a classic-style class is modified to implement an
94
 
interface.
95
 
 
96
 
    >>> class SomeClassicClass:
97
 
    ...     decorates(IFoo)
98
 
    Traceback (most recent call last):
99
 
    ...
100
 
    TypeError: Cannot use decorates() on a classic
101
 
        class: __builtin__.SomeClassicClass.
102
 
 
103
 
The decorates function cannot be used out side of a class definition,
104
 
such as in a module or in a function.
105
 
 
106
 
    >>> decorates(IFoo)
107
 
    Traceback (most recent call last):
108
 
    ...
109
 
    TypeError: Decorates can be used only from a class definition.
110
 
 
111
 
 
112
 
== Passthrough ==
113
 
 
114
 
The Passthrough class is the implementation machinery of decorates.
115
 
It uses the descriptor protocol to implement the delegation behaviour
116
 
provided by decorates. It takes two arguments, the name of the attribute
117
 
that is delegated, and the name of the attribute containing the object
118
 
to delegate to.
119
 
 
120
 
To illustrate, p and p2 are two Passthrough instances that use the
121
 
instance assigned to 'mycontext' to call the 'foo' attribute and
122
 
the 'clsmethod' method.
123
 
 
124
 
    >>> from canonical.lazr import Passthrough
125
 
    >>> p = Passthrough('foo', 'mycontext')
126
 
    >>> p2 = Passthrough('clsmethod', 'mycontext')
127
 
 
128
 
Base is a class the implements both 'foo' and 'clsmethod'.
129
 
 
130
 
    >>> class Base:
131
 
    ...     foo = 'foo from Base'
132
 
    ...     def clsmethod(cls):
133
 
    ...         return str(cls)
134
 
    ...     clsmethod = classmethod(clsmethod)
135
 
 
136
 
Adapter is a class that has an instance of Base assigned to the
137
 
attribute 'mycontext'.
138
 
 
139
 
    >>> base = Base()
140
 
 
141
 
    >>> class Adapter:
142
 
    ...     mycontext = base
143
 
 
144
 
    >>> adapter = Adapter()
145
 
 
146
 
The Passthrough instances can get and set their prescribed attributes
147
 
when passed an instance of adapter.
148
 
 
149
 
    >>> p.__get__(adapter)
150
 
    'foo from Base'
151
 
 
152
 
    >>> p.__get__(None, Adapter) is p
153
 
    True
154
 
 
155
 
    >>> p2.__get__(adapter)()
156
 
    '__builtin__.Base'
157
 
 
158
 
    >>> p.__set__(adapter, 'new value')
159
 
    >>> base.foo
160
 
    'new value'
161
 
 
162
 
Passthrough does not implement __delete__. An error is raised if
163
 
it is called.
164
 
 
165
 
    >>> p.__delete__(adapter)
166
 
    Traceback (most recent call last):
167
 
    ...
168
 
    NotImplementedError
169