~certify-web-dev/twisted/certify-staging

« back to all changes in this revision

Viewing changes to doc/howto/pb-copyable.html

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2010-01-02 19:38:17 UTC
  • mfrom: (2.2.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100102193817-jphp464ppwh7dulg
Tags: 9.0.0-1
* python-twisted: Depend on the python-twisted-* 9.0 packages.
* python-twisted: Depend on python-zope.interface only. Closes: #557781.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2
 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><title>Twisted Documentation: PB Copyable: Passing Complex Types</title><link href="stylesheet.css" type="text/css" rel="stylesheet" /></head><body bgcolor="white"><h1 class="title">PB Copyable: Passing Complex Types</h1><div class="toc"><ol><li><a href="#auto0">Overview</a></li><li><a href="#auto1">Motivation</a></li><li><a href="#auto2">Passing Objects</a></li><ul><li><a href="#auto3">Security Options</a></li><li><a href="#auto4">What class to use?</a></li></ul><li><a href="#auto5">pb.Copyable</a></li><ul><li><a href="#auto6">Controlling the Copied State</a></li><li><a href="#auto7">Things To Watch Out For</a></li><li><a href="#auto8">More Information</a></li></ul><li><a href="#auto9">pb.Cacheable</a></li><ul><li><a href="#auto10">Example</a></li><li><a href="#auto11">More Information</a></li></ul></ol></div><div class="content"><span></span><h2>Overview<a name="auto0"></a></h2><p>This chapter focuses on how to use PB to pass complex types (specifically
 
1
<?xml version="1.0" encoding="utf-8"?>
 
2
<!DOCTYPE html
 
3
  PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN'
 
4
  'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
 
5
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 
6
  <head>
 
7
<title>Twisted Documentation: PB Copyable: Passing Complex Types</title>
 
8
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
 
9
  </head>
 
10
 
 
11
  <body bgcolor="white">
 
12
    <h1 class="title">PB Copyable: Passing Complex Types</h1>
 
13
    <div class="toc"><ol><li><a href="#auto0">Overview</a></li><li><a href="#auto1">Motivation</a></li><li><a href="#auto2">Passing Objects</a></li><ul><li><a href="#auto3">Security Options</a></li><li><a href="#auto4">What class to use?</a></li></ul><li><a href="#auto5">pb.Copyable</a></li><ul><li><a href="#auto6">Controlling the Copied State</a></li><li><a href="#auto7">Things To Watch Out For</a></li><li><a href="#auto8">More Information</a></li></ul><li><a href="#auto9">pb.Cacheable</a></li><ul><li><a href="#auto10">Example</a></li><li><a href="#auto11">More Information</a></li></ul></ol></div>
 
14
    <div class="content">
 
15
<span/>
 
16
 
 
17
<h2>Overview<a name="auto0"/></h2>
 
18
 
 
19
<p>This chapter focuses on how to use PB to pass complex types (specifically
3
20
class instances) to and from a remote process. The first section is on
4
 
simply copying the contents of an object to a remote process (<code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>). The second covers how
5
 
to copy those contents once, then update them later when they change (<code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">Cacheable</a></code>).</p><h2>Motivation<a name="auto1"></a></h2><p>From the <a href="pb-usage.html">previous chapter</a>, you've seen how to
 
21
simply copying the contents of an object to a remote process (<code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>). The second covers how
 
22
to copy those contents once, then update them later when they change (<code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">Cacheable</a></code>).</p>
 
23
 
 
24
<h2>Motivation<a name="auto1"/></h2>
 
25
 
 
26
<p>From the <a href="pb-usage.html" shape="rect">previous chapter</a>, you've seen how to
6
27
pass basic types to a remote process, by using them in the arguments or
7
 
return values of a <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Referenceable.callRemote.html" title="twisted.spread.pb.Referenceable.callRemote">callRemote</a></code> function. However,
 
28
return values of a <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Referenceable.callRemote.html" title="twisted.spread.pb.Referenceable.callRemote">callRemote</a></code> function. However,
8
29
if you've experimented with it, you may have discovered problems when trying
9
30
to pass anything more complicated than a primitive int/list/dict/string
10
 
type, or another <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Referenceable.html" title="twisted.spread.pb.Referenceable">pb.Referenceable</a></code> object. At some point you want
 
31
type, or another <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Referenceable.html" title="twisted.spread.pb.Referenceable">pb.Referenceable</a></code> object. At some point you want
11
32
to pass entire objects between processes, instead of having to reduce them
12
33
down to dictionaries on one end and then re-instantiating them on the
13
 
other.</p><h2>Passing Objects<a name="auto2"></a></h2><p>The most obvious and straightforward way to send an object to a remote
 
34
other.</p>
 
35
 
 
36
<h2>Passing Objects<a name="auto2"/></h2>
 
37
 
 
38
<p>The most obvious and straightforward way to send an object to a remote
14
39
process is with something like the following code. It also happens that this
15
 
code doesn't work, as will be explained below.</p><pre class="python">
16
 
<span class="py-src-keyword">class</span> <span class="py-src-identifier">LilyPond</span>:
 
40
code doesn't work, as will be explained below.</p>
 
41
 
 
42
<pre class="python"><p class="py-linenumber">1
 
43
2
 
44
3
 
45
4
 
46
5
 
47
6
 
48
</p><span class="py-src-keyword">class</span> <span class="py-src-identifier">LilyPond</span>:
17
49
  <span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">frogs</span>):
18
50
    <span class="py-src-variable">self</span>.<span class="py-src-variable">frogs</span> = <span class="py-src-variable">frogs</span>
19
51
 
20
52
<span class="py-src-variable">pond</span> = <span class="py-src-variable">LilyPond</span>(<span class="py-src-number">12</span>)
21
53
<span class="py-src-variable">ref</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">&quot;sendPond&quot;</span>, <span class="py-src-variable">pond</span>)
22
 
</pre><p>If you try to run this, you might hope that a suitable remote end which
 
54
</pre>
 
55
 
 
56
<p>If you try to run this, you might hope that a suitable remote end which
23
57
implements the <code>remote_sendPond</code> method would see that method get
24
58
invoked with an instance from the <code>LilyPond</code> class. But instead,
25
 
you'll encounter the dreaded <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.jelly.InsecureJelly.html" title="twisted.spread.jelly.InsecureJelly">InsecureJelly</a></code> exception. This is
 
59
you'll encounter the dreaded <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.jelly.InsecureJelly.html" title="twisted.spread.jelly.InsecureJelly">InsecureJelly</a></code> exception. This is
26
60
Twisted's way of telling you that you've violated a security restriction,
27
 
and that the receiving end refuses to accept your object.</p><h3>Security Options<a name="auto3"></a></h3><p>What's the big deal? What's wrong with just copying a class into another
28
 
process' namespace?</p><p>Reversing the question might make it easier to see the issue: what is the
 
61
and that the receiving end refuses to accept your object.</p>
 
62
 
 
63
<h3>Security Options<a name="auto3"/></h3>
 
64
 
 
65
<p>What's the big deal? What's wrong with just copying a class into another
 
66
process' namespace?</p>
 
67
 
 
68
<p>Reversing the question might make it easier to see the issue: what is the
29
69
problem with accepting a stranger's request to create an arbitrary object in
30
70
your local namespace? The real question is how much power you are granting
31
71
them: what actions can they convince you to take on the basis of the bytes
32
 
they are sending you over that remote connection.</p><p>Objects generally represent more power than basic types like strings and
 
72
they are sending you over that remote connection.</p>
 
73
 
 
74
<p>Objects generally represent more power than basic types like strings and
33
75
dictionaries because they also contain (or reference) code, which can modify
34
76
other data structures when executed. Once previously-trusted data is
35
 
subverted, the rest of the program is compromised.</p><p>The built-in Python <q>batteries included</q> classes are relatively
 
77
subverted, the rest of the program is compromised.</p>
 
78
 
 
79
<p>The built-in Python <q>batteries included</q> classes are relatively
36
80
tame, but you still wouldn't want to let a foreign program use them to
37
81
create arbitrary objects in your namespace or on your computer. Imagine a
38
82
protocol that involved sending a file-like object with a <code>read()</code>
39
83
method that was supposed to used later to retrieve a document. Then imagine
40
84
what if that object were created with
41
85
<code>os.fdopen(&quot;~/.gnupg/secring.gpg&quot;)</code>. Or an instance of
42
 
<code>telnetlib.Telnet(&quot;localhost&quot;, &quot;chargen&quot;)</code>. </p><p>Classes you've written for your own program are likely to have far more
 
86
<code>telnetlib.Telnet(&quot;localhost&quot;, &quot;chargen&quot;)</code>. </p>
 
87
 
 
88
<p>Classes you've written for your own program are likely to have far more
43
89
power. They may run code during <code>__init__</code>, or even have special
44
90
meaning simply because of their existence. A program might have
45
91
<code>User</code> objects to represent user accounts, and have a rule that
48
94
would probably add the object to a global list of known users). The simple
49
95
act of creating an object would give access to somebody. If you could be
50
96
tricked into creating a bad object, an unauthorized user would get
51
 
access.</p><p>So object creation needs to be part of a system's security design. The
 
97
access.</p>
 
98
 
 
99
<p>So object creation needs to be part of a system's security design. The
52
100
dotted line between <q>trusted inside</q> and <q>untrusted outside</q> needs
53
101
to describe what may be done in response to outside events. One of those
54
102
events is the receipt of an object through a PB remote procedure call, which
55
103
is a request to create an object in your <q>inside</q> namespace. The
56
104
question is what to do in response to it. For this reason, you must
57
105
explicitly specific what remote classes will be accepted, and how their
58
 
local representatives are to be created.</p><h3>What class to use?<a name="auto4"></a></h3><p>Another basic question to answer before we can do anything useful with an
 
106
local representatives are to be created.</p>
 
107
 
 
108
<h3>What class to use?<a name="auto4"/></h3>
 
109
 
 
110
<p>Another basic question to answer before we can do anything useful with an
59
111
incoming serialized object is: what class should we create? The simplistic
60
112
answer is to create the <q>same kind</q> that was serialized on the sender's
61
113
end of the wire, but this is not as easy or as straightforward as you might
65
117
guarantee that the sender is even running Python! All we know on the
66
118
receiving end is a list of two things which describe the instance they are
67
119
trying to send us: the name of the class, and a representation of the
68
 
contents of the object.</p><p>PB lets you specify the mapping from remote class names to local classes
69
 
with the <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.jelly.setUnjellyableForClass.html" title="twisted.spread.jelly.setUnjellyableForClass">setUnjellyableForClass</a></code> function<!--
70
 
 
71
 
--><a href="#footnote-1" title="Note that, in this context, unjelly is a verb with the opposite meaning of jelly. The verb to jelly means to serialize an object or data structure into a sequence of bytes (or other primitive transmittable/storable representation), while to unjelly means to unserialize the bytestream into a live object in the receiver's memory space. Unjellyable is a noun, (not an adjective), referring to the the class that serves as a destination or recipient of the unjellying process. A is unjellyable into B means that a serialized representation A (of some remote object) can be unserialized into a local object of type B. It is these objects B that are the Unjellyable second argument of the setUnjellyableForClass function.In particular, unjellyable does not mean cannot be jellied. Unpersistable means not persistable, but unjelly, unserialize, and unpickle mean to reverse the operations of jellying, serializing, and pickling."><super>1</super></a>.
 
120
contents of the object.</p>
 
121
 
 
122
 
 
123
<p>PB lets you specify the mapping from remote class names to local classes
 
124
with the <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.jelly.setUnjellyableForClass.html" title="twisted.spread.jelly.setUnjellyableForClass">setUnjellyableForClass</a></code> function<a href="#footnote-1" title="Note that, in this context, unjelly is a verb with the opposite meaning of jelly. The verb to jelly means to serialize an object or data structure into a sequence of bytes (or other primitive transmittable/storable representation), while to unjelly means to unserialize the bytestream into a live object in the receiver's memory space. Unjellyable is a noun, (not an adjective), referring to the the class that serves as a destination or recipient of the unjellying process. A is unjellyable into B means that a serialized representation A (of some remote object) can be unserialized into a local object of type B. It is these objects B that are the Unjellyable second argument of the setUnjellyableForClass function. In particular, unjellyable does not mean cannot be jellied. Unpersistable means not persistable, but unjelly, unserialize, and unpickle mean to reverse the operations of jellying, serializing, and pickling."><super>1</super></a>.
72
125
 
73
126
 
74
127
This function takes a remote/sender class reference (either the
78
131
the remote end sends an object, the class name that they transmit is looked
79
132
up in the table controlled by this function. If a matching class is found,
80
133
it is used to create the local object. If not, you get the
81
 
<code>InsecureJelly</code> exception.</p><p>In general you expect both ends to share the same codebase: either you
 
134
<code>InsecureJelly</code> exception.</p>
 
135
 
 
136
<p>In general you expect both ends to share the same codebase: either you
82
137
control the program that is running on both ends of the wire, or both
83
138
programs share some kind of common language that is implemented in code
84
139
which exists on both ends. You wouldn't expect them to send you an object of
90
145
through namespace collisions between unrelated packages, version skew
91
146
between nodes that haven't been updated at the same rate, or a malicious
92
147
intruder trying to cause your code to fail in some interesting or
93
 
potentially vulnerable way.</p><h2>pb.Copyable<a name="auto5"></a></h2><p>Ok, enough of this theory. How do you send a fully-fledged object from
94
 
one side to the other?</p><div class="py-listing"><pre>
95
 
<span class="py-src-comment">#! /usr/bin/python
96
 
</span>
 
148
potentially vulnerable way.</p>
 
149
 
 
150
 
 
151
<h2>pb.Copyable<a name="auto5"/></h2>
 
152
 
 
153
<p>Ok, enough of this theory. How do you send a fully-fledged object from
 
154
one side to the other?</p>
 
155
 
 
156
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
157
 2
 
158
 3
 
159
 4
 
160
 5
 
161
 6
 
162
 7
 
163
 8
 
164
 9
 
165
10
 
166
11
 
167
12
 
168
13
 
169
14
 
170
15
 
171
16
 
172
17
 
173
18
 
174
19
 
175
20
 
176
21
 
177
22
 
178
23
 
179
24
 
180
25
 
181
26
 
182
27
 
183
28
 
184
29
 
185
30
 
186
31
 
187
32
 
188
33
 
189
34
 
190
35
 
191
36
 
192
37
 
193
38
 
194
39
 
195
40
 
196
41
 
197
42
 
198
43
 
199
44
 
200
45
 
201
46
 
202
47
 
203
48
 
204
49
 
205
50
 
206
51
 
207
52
 
208
53
 
209
54
 
210
55
 
211
56
 
212
57
 
213
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
214
 
 
215
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
216
<span class="py-src-comment"># See LICENSE for details.</span>
 
217
 
97
218
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>, <span class="py-src-variable">jelly</span>
98
219
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
99
220
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
134
255
    <span class="py-src-variable">pond</span> = <span class="py-src-variable">CopyPond</span>()
135
256
    <span class="py-src-variable">pond</span>.<span class="py-src-variable">setStuff</span>(<span class="py-src-string">&quot;green&quot;</span>, <span class="py-src-number">7</span>)
136
257
    <span class="py-src-variable">pond</span>.<span class="py-src-variable">countFrogs</span>()
137
 
    <span class="py-src-comment"># class name:
138
 
</span>    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;.&quot;</span>.<span class="py-src-variable">join</span>([<span class="py-src-variable">pond</span>.<span class="py-src-variable">__class__</span>.<span class="py-src-variable">__module__</span>, <span class="py-src-variable">pond</span>.<span class="py-src-variable">__class__</span>.<span class="py-src-variable">__name__</span>])
 
258
    <span class="py-src-comment"># class name:</span>
 
259
    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;.&quot;</span>.<span class="py-src-variable">join</span>([<span class="py-src-variable">pond</span>.<span class="py-src-variable">__class__</span>.<span class="py-src-variable">__module__</span>, <span class="py-src-variable">pond</span>.<span class="py-src-variable">__class__</span>.<span class="py-src-variable">__name__</span>])
139
260
 
140
261
    <span class="py-src-variable">sender</span> = <span class="py-src-variable">Sender</span>(<span class="py-src-variable">pond</span>)
141
262
    <span class="py-src-variable">factory</span> = <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBClientFactory</span>()
146
267
 
147
268
<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
148
269
    <span class="py-src-variable">main</span>()
149
 
</pre><div class="caption">Source listing - <a href="listings/pb/copy_sender.py"><span class="filename">listings/pb/copy_sender.py</span></a></div></div><div class="py-listing"><pre>
150
 
<span class="py-src-string">&quot;&quot;&quot;PB copy receiver example.
 
270
</pre><div class="caption">Source listing - <a href="listings/pb/copy_sender.py"><span class="filename">listings/pb/copy_sender.py</span></a></div></div>
 
271
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
272
 2
 
273
 3
 
274
 4
 
275
 5
 
276
 6
 
277
 7
 
278
 8
 
279
 9
 
280
10
 
281
11
 
282
12
 
283
13
 
284
14
 
285
15
 
286
16
 
287
17
 
288
18
 
289
19
 
290
20
 
291
21
 
292
22
 
293
23
 
294
24
 
295
25
 
296
26
 
297
27
 
298
28
 
299
29
 
300
30
 
301
31
 
302
32
 
303
33
 
304
34
 
305
35
 
306
36
 
307
37
 
308
38
 
309
39
 
310
40
 
311
41
 
312
</p><span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
313
<span class="py-src-comment"># See LICENSE for details.</span>
 
314
 
 
315
<span class="py-src-string">&quot;&quot;&quot;
 
316
PB copy receiver example.
151
317
 
152
318
This is a Twisted Application Configuration (tac) file.  Run with e.g.
153
319
   twistd -ny copy_receiver.tac
 
320
 
154
321
See the twistd(1) man page or
155
322
http://twistedmatrix.com/documents/current/howto/application for details.
156
323
&quot;&quot;&quot;</span>
166
333
<span class="py-src-keyword">from</span> <span class="py-src-variable">copy_sender</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">LilyPond</span>, <span class="py-src-variable">CopyPond</span>
167
334
 
168
335
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
169
 
<span class="py-src-comment">#log.startLogging(sys.stdout)
170
 
</span>
 
336
<span class="py-src-comment">#log.startLogging(sys.stdout)</span>
 
337
 
171
338
<span class="py-src-keyword">class</span> <span class="py-src-identifier">ReceiverPond</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">RemoteCopy</span>, <span class="py-src-parameter">LilyPond</span>):
172
339
    <span class="py-src-keyword">pass</span>
173
340
<span class="py-src-variable">pb</span>.<span class="py-src-variable">setUnjellyableForClass</span>(<span class="py-src-variable">CopyPond</span>, <span class="py-src-variable">ReceiverPond</span>)
183
350
<span class="py-src-variable">application</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">Application</span>(<span class="py-src-string">&quot;copy_receiver&quot;</span>)
184
351
<span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">8800</span>, <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">Receiver</span>())).<span class="py-src-variable">setServiceParent</span>(
185
352
    <span class="py-src-variable">service</span>.<span class="py-src-variable">IServiceCollection</span>(<span class="py-src-variable">application</span>))
186
 
</pre><div class="caption">Source listing - <a href="listings/pb/copy_receiver.tac"><span class="filename">listings/pb/copy_receiver.tac</span></a></div></div><p>The sending side has a class called <code>LilyPond</code>. To make this
 
353
</pre><div class="caption">Source listing - <a href="listings/pb/copy_receiver.tac"><span class="filename">listings/pb/copy_receiver.tac</span></a></div></div>
 
354
 
 
355
<p>The sending side has a class called <code>LilyPond</code>. To make this
187
356
eligble for transport through <code>callRemote</code> (either as an
188
357
argument, a return value, or something referenced by either of those [like a
189
 
dictionary value]), it must inherit from one of the four <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.Serializable.html" title="twisted.spread.flavors.Serializable">Serializable</a></code> classes. In this section,
190
 
we focus on <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.Copyable.html" title="twisted.spread.flavors.Copyable">Copyable</a></code>.
 
358
dictionary value]), it must inherit from one of the four <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.Serializable.html" title="twisted.spread.flavors.Serializable">Serializable</a></code> classes. In this section,
 
359
we focus on <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.Copyable.html" title="twisted.spread.flavors.Copyable">Copyable</a></code>.
191
360
The copyable subclass of <code>LilyPond</code> is called
192
361
<code>CopyPond</code>. We create an instance of it and send it through
193
362
<code>callRemote</code> as an argument to the receiver's
196
365
<q>copy_sender.CopyPond</q> and some chunk of data that represents the
197
366
object's state. <code>pond.__class__.__module__</code> and
198
367
<code>pond.__class__.__name__</code> are used to derive the class name
199
 
string. The object's <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.flavors.Copyable.getStateToCopy.html" title="twisted.spread.pb.flavors.Copyable.getStateToCopy">getStateToCopy</a></code> method is
200
 
used to get the state: this is provided by <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>, and the default just retrieves
 
368
string. The object's <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.flavors.Copyable.getStateToCopy.html" title="twisted.spread.pb.flavors.Copyable.getStateToCopy">getStateToCopy</a></code> method is
 
369
used to get the state: this is provided by <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>, and the default just retrieves
201
370
<code>self.__dict__</code>. This works just like the optional
202
371
<code>__getstate__</code> method used by <code>pickle</code>. The pair of
203
 
name and state are sent over the wire to the receiver.</p><p>The receiving end defines a local class named <code>ReceiverPond</code>
 
372
name and state are sent over the wire to the receiver.</p>
 
373
 
 
374
<p>The receiving end defines a local class named <code>ReceiverPond</code>
204
375
to represent incoming <code>LilyPond</code> instances. This class derives
205
376
from the sender's <code>LilyPond</code> class (with a fully-qualified name
206
377
of <code>copy_sender.LilyPond</code>), which specifies how we expect it to
207
378
behave. We trust that this is the same <code>LilyPond</code> class as the
208
379
sender used. (At the very least, we hope ours will be able to accept a state
209
 
created by theirs). It also inherits from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code>, which is a requirement for all
 
380
created by theirs). It also inherits from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code>, which is a requirement for all
210
381
classes that act in this local-representative role (those which are given to
211
382
the second argument of <code>setUnjellyableForClass</code>).
212
383
<code>RemoteCopy</code> provides the methods that tell the Jelly layer how
213
 
to create the local object from the incoming serialized state.</p><p>Then <code>setUnjellyableForClass</code> is used to register the two
 
384
to create the local object from the incoming serialized state.</p>
 
385
 
 
386
<p>Then <code>setUnjellyableForClass</code> is used to register the two
214
387
classes. This has two effects: instances of the remote class (the first
215
388
argument) will be allowed in through the security layer, and instances of
216
389
the local class (the second argument) will be used to contain the state that
217
 
is transmitted when the sender serializes the remote object.</p><p>When the receiver unserializes (<q>unjellies</q>) the object, it will
 
390
is transmitted when the sender serializes the remote object.</p>
 
391
 
 
392
<p>When the receiver unserializes (<q>unjellies</q>) the object, it will
218
393
create an instance of the local <code>ReceiverPond</code> class, and hand
219
394
the transmitted state (usually in the form of a dictionary) to that object's
220
 
<code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.flavors.RemoteCopy.setCopyableState.html" title="twisted.spread.pb.flavors.RemoteCopy.setCopyableState">setCopyableState</a></code> method.
 
395
<code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.flavors.RemoteCopy.setCopyableState.html" title="twisted.spread.pb.flavors.RemoteCopy.setCopyableState">setCopyableState</a></code> method.
221
396
This acts just like the <code>__setstate__</code> method that
222
397
<code>pickle</code> uses when unserializing an object.
223
398
<code>getStateToCopy</code>/<code>setCopyableState</code> are distinct from
224
399
<code>__getstate__</code>/<code>__setstate__</code> to allow objects to be
225
400
persisted (across time) differently than they are transmitted (across
226
 
[memory]space).</p><p>When this is run, it produces the following output:</p><pre class="shell">
 
401
[memory]space).</p>
 
402
 
 
403
<p>When this is run, it produces the following output:</p>
 
404
 
 
405
<pre class="shell" xml:space="preserve">
227
406
[-] twisted.spread.pb.PBServerFactory starting on 8800
228
407
[-] Starting factory &lt;twisted.spread.pb.PBServerFactory instance at
229
408
0x406159cc&gt;
230
409
[Broker,0,127.0.0.1]  got pond: &lt;__builtin__.ReceiverPond instance at
231
410
0x406ec5ec&gt;
232
411
[Broker,0,127.0.0.1] 7 frogs
233
 
</pre><pre class="shell">
 
412
</pre>
 
413
 
 
414
<pre class="shell" xml:space="preserve">
234
415
% ./copy_sender.py 
235
416
7 frogs
236
417
copy_sender.CopyPond
237
418
pond arrived safe and sound
238
419
Main loop terminated.
239
420
%
240
 
</pre><h3>Controlling the Copied State<a name="auto6"></a></h3><p>By overriding <code>getStateToCopy</code> and
 
421
</pre>
 
422
 
 
423
 
 
424
 
 
425
<h3>Controlling the Copied State<a name="auto6"/></h3>
 
426
 
 
427
<p>By overriding <code>getStateToCopy</code> and
241
428
<code>setCopyableState</code>, you can control how the object is transmitted
242
429
over the wire. For example, you might want perform some data-reduction:
243
430
pre-compute some results instead of sending all the raw data over the wire.
244
431
Or you could replace references to a local object on the sender's side with
245
432
markers before sending, then upon receipt replace those markers with
246
433
references to a receiver-side proxy that could perform the same operations
247
 
against a local cache of data.</p><p>Another good use for <code>getStateToCopy</code> is to implement
 
434
against a local cache of data.</p>
 
435
 
 
436
<p>Another good use for <code>getStateToCopy</code> is to implement
248
437
<q>local-only</q> attributes: data that is only accessible by the local
249
438
process, not to any remote users. For example, a <code>.password</code>
250
439
attribute could be removed from the object state before sending to a remote
252
441
unchanged from a round trip, this could be used to build a
253
442
challenge-response system (in fact PB does this with
254
443
<code>pb.Referenceable</code> objects to implement authorization as
255
 
described <a href="pb-cred.html">here</a>).</p><p>Whatever <code>getStateToCopy</code> returns from the sending object will
 
444
described <a href="pb-cred.html" shape="rect">here</a>).</p>
 
445
 
 
446
<p>Whatever <code>getStateToCopy</code> returns from the sending object will
256
447
be serialized and sent over the wire; <code>setCopyableState</code> gets
257
448
whatever comes over the wire and is responsible for setting up the state of
258
 
the object it lives in.</p><div class="py-listing"><pre>
259
 
<span class="py-src-comment">#! /usr/bin/python
260
 
</span>
 
449
the object it lives in.</p>
 
450
 
 
451
 
 
452
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
453
 2
 
454
 3
 
455
 4
 
456
 5
 
457
 6
 
458
 7
 
459
 8
 
460
 9
 
461
10
 
462
11
 
463
12
 
464
13
 
465
14
 
466
15
 
467
16
 
468
17
 
469
18
 
470
19
 
471
20
 
472
21
 
473
22
 
474
23
 
475
24
 
476
25
 
477
26
 
478
27
 
479
28
 
480
29
 
481
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
482
 
 
483
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
484
<span class="py-src-comment"># See LICENSE for details.</span>
 
485
 
261
486
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
262
487
 
263
488
<span class="py-src-keyword">class</span> <span class="py-src-identifier">FrogPond</span>:
282
507
        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">frogsAndToads</span>
283
508
 
284
509
<span class="py-src-variable">pb</span>.<span class="py-src-variable">setUnjellyableForClass</span>(<span class="py-src-variable">SenderPond</span>, <span class="py-src-variable">ReceiverPond</span>)
285
 
</pre><div class="caption">Source listing - <a href="listings/pb/copy2_classes.py"><span class="filename">listings/pb/copy2_classes.py</span></a></div></div><div class="py-listing"><pre>
286
 
<span class="py-src-comment">#! /usr/bin/python
287
 
</span>
 
510
</pre><div class="caption">Source listing - <a href="listings/pb/copy2_classes.py"><span class="filename">listings/pb/copy2_classes.py</span></a></div></div>
 
511
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
512
 2
 
513
 3
 
514
 4
 
515
 5
 
516
 6
 
517
 7
 
518
 8
 
519
 9
 
520
10
 
521
11
 
522
12
 
523
13
 
524
14
 
525
15
 
526
16
 
527
17
 
528
18
 
529
19
 
530
20
 
531
21
 
532
22
 
533
23
 
534
24
 
535
25
 
536
26
 
537
27
 
538
28
 
539
29
 
540
30
 
541
31
 
542
32
 
543
33
 
544
34
 
545
35
 
546
36
 
547
37
 
548
38
 
549
39
 
550
40
 
551
41
 
552
42
 
553
43
 
554
44
 
555
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
556
 
 
557
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
558
<span class="py-src-comment"># See LICENSE for details.</span>
 
559
 
288
560
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>, <span class="py-src-variable">jelly</span>
289
561
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
290
562
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
323
595
 
324
596
<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
325
597
    <span class="py-src-variable">main</span>()
326
 
</pre><div class="caption">Source listing - <a href="listings/pb/copy2_sender.py"><span class="filename">listings/pb/copy2_sender.py</span></a></div></div><div class="py-listing"><pre>
327
 
<span class="py-src-comment">#! /usr/bin/python
328
 
</span>
 
598
</pre><div class="caption">Source listing - <a href="listings/pb/copy2_sender.py"><span class="filename">listings/pb/copy2_sender.py</span></a></div></div>
 
599
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
600
 2
 
601
 3
 
602
 4
 
603
 5
 
604
 6
 
605
 7
 
606
 8
 
607
 9
 
608
10
 
609
11
 
610
12
 
611
13
 
612
14
 
613
15
 
614
16
 
615
17
 
616
18
 
617
19
 
618
20
 
619
21
 
620
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
621
 
 
622
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
623
<span class="py-src-comment"># See LICENSE for details.</span>
 
624
 
329
625
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">service</span>, <span class="py-src-variable">internet</span>
330
626
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
331
627
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
342
638
<span class="py-src-variable">application</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">Application</span>(<span class="py-src-string">&quot;copy_receiver&quot;</span>)
343
639
<span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">8800</span>, <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">Receiver</span>())).<span class="py-src-variable">setServiceParent</span>(
344
640
    <span class="py-src-variable">service</span>.<span class="py-src-variable">IServiceCollection</span>(<span class="py-src-variable">application</span>))
345
 
</pre><div class="caption">Source listing - <a href="listings/pb/copy2_receiver.py"><span class="filename">listings/pb/copy2_receiver.py</span></a></div></div><p>In this example, the classes are defined in a separate source file, which
 
641
</pre><div class="caption">Source listing - <a href="listings/pb/copy2_receiver.py"><span class="filename">listings/pb/copy2_receiver.py</span></a></div></div>
 
642
 
 
643
<p>In this example, the classes are defined in a separate source file, which
346
644
also sets up the binding between them. The <code>SenderPond</code> and
347
645
<code>ReceiverPond</code> are unrelated save for this binding: they happen
348
646
to implement the same methods, but use different internal instance variables
349
 
to accomplish them.</p><p>The recipient of the object doesn't even have to import the class
 
647
to accomplish them.</p>
 
648
 
 
649
<p>The recipient of the object doesn't even have to import the class
350
650
definition into their namespace. It is sufficient that they import the class
351
651
definition (and thus execute the <code>setUnjellyableForClass</code>
352
652
statement). The Jelly layer remembers the class definition until a matching
353
653
object is received. The sender of the object needs the definition, of
354
 
course, to create the object in the first place.</p><p>When run, the <code>copy2</code> example emits the following:</p><pre class="shell">
 
654
course, to create the object in the first place.</p>
 
655
 
 
656
<p>When run, the <code>copy2</code> example emits the following:</p>
 
657
 
 
658
<pre class="shell" xml:space="preserve">
355
659
% twistd -n -y copy2_receiver.py 
356
660
[-] twisted.spread.pb.PBServerFactory starting on 8800
357
661
[-] Starting factory &lt;twisted.spread.pb.PBServerFactory instance at
359
663
[Broker,0,127.0.0.1]  got pond: &lt;copy2_classes.ReceiverPond instance at
360
664
0x406eb2ac&gt;
361
665
[Broker,0,127.0.0.1]  count 7
362
 
</pre><pre class="shell">
 
666
</pre>
 
667
 
 
668
<pre class="shell" xml:space="preserve">
363
669
% ./copy2_sender.py 
364
670
count 7
365
671
pond arrived safe and sound
366
672
Main loop terminated.
367
673
368
 
</pre><h3>Things To Watch Out For<a name="auto7"></a></h3><ul><li>The first argument to <code>setUnjellyableForClass</code> must refer
 
674
</pre>
 
675
 
 
676
 
 
677
 
 
678
<h3>Things To Watch Out For<a name="auto7"/></h3>
 
679
 
 
680
<ul>
 
681
 
 
682
  <li>The first argument to <code>setUnjellyableForClass</code> must refer
369
683
  to the class <em>as known by the sender</em>. The sender has no way of
370
684
  knowing about how your local <code>import</code> statements are set up,
371
685
  and Python's flexible namespace semantics allow you to access the same
376
690
  defined together, with the <code>setUnjellyableForClass</code> immediately
377
691
  following them. (XXX: this works, but does this really get the right names
378
692
  into the table? Or does it only work because both are defined in the same
379
 
  (wrong) place?)</li><li>The class that is sent must inherit from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>. The class that is registered to
380
 
  receive it must inherit from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code><a href="#footnote-2" title="pb.RemoteCopy is actually defined as flavors.RemoteCopy, but pb.RemoteCopy is the preferred way to access it"><super>2</super></a>. </li><li>The same class can be used to send and receive. Just have it inherit
 
693
  (wrong) place?)</li>
 
694
 
 
695
  <li>The class that is sent must inherit from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>. The class that is registered to
 
696
  receive it must inherit from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code><a href="#footnote-2" title="pb.RemoteCopy is actually defined as flavors.RemoteCopy, but pb.RemoteCopy is the preferred way to access it"><super>2</super></a>. </li>
 
697
 
 
698
  <li>The same class can be used to send and receive. Just have it inherit
381
699
  from both <code>pb.Copyable</code> and <code>pb.RemoteCopy</code>. This
382
700
  will also make it possible to send the same class symmetrically back and
383
701
  forth over the wire. But don't get confused about when it is coming (and
384
702
  using <code>setCopyableState</code>) versus when it is going (using
385
 
  <code>getStateToCopy</code>).</li><li><code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.jelly.InsecureJelly.html" title="twisted.spread.jelly.InsecureJelly">InsecureJelly</a></code>
 
703
  <code>getStateToCopy</code>).</li>
 
704
 
 
705
  <li><code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.jelly.InsecureJelly.html" title="twisted.spread.jelly.InsecureJelly">InsecureJelly</a></code>
386
706
  exceptions are raised by the receiving end. They will be delivered
387
707
  asynchronously to an <code>errback</code> handler. If you do not add one
388
708
  to the <code>Deferred</code> returned by <code>callRemote</code>, then you
389
 
  will never receive notification of the problem. </li><li>The class that is derived from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code> will be created using a
 
709
  will never receive notification of the problem. </li>
 
710
 
 
711
  <li>The class that is derived from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code> will be created using a
390
712
  constructor <code>__init__</code> method that takes no arguments. All
391
713
  setup must be performed in the <code>setCopyableState</code> method. As
392
 
  the docstring on <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.RemoteCopy.html" title="twisted.spread.flavors.RemoteCopy">RemoteCopy</a></code> says, don't implement a
 
714
  the docstring on <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.RemoteCopy.html" title="twisted.spread.flavors.RemoteCopy">RemoteCopy</a></code> says, don't implement a
393
715
  constructor that requires arguments in a subclass of
394
716
  <code>RemoteCopy</code>. XXX: check this, the code around
395
717
  jelly._Unjellier.unjelly:489 tries to avoid calling <code>__init__</code>
396
 
  just in case the constructor requires args. </li></ul><h3>More Information<a name="auto8"></a></h3><ul><li><code>pb.Copyable</code> is mostly implemented in <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.html" title="twisted.spread.flavors">twisted.spread.flavors</a></code>, and the docstrings there are the
397
 
  best source of additional information.</li><li><code>Copyable</code> is also used in <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.web.distrib.html" title="twisted.web.distrib">twisted.web.distrib</a></code> to deliver HTTP requests to other
 
718
  just in case the constructor requires args. </li>
 
719
  
 
720
</ul>
 
721
 
 
722
<h3>More Information<a name="auto8"/></h3>
 
723
 
 
724
<ul>
 
725
 
 
726
  <li> <code>pb.Copyable</code> is mostly implemented in <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.html" title="twisted.spread.flavors">twisted.spread.flavors</a></code>, and the docstrings there are the
 
727
  best source of additional information.</li>
 
728
 
 
729
  <li><code>Copyable</code> is also used in <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.web.distrib.html" title="twisted.web.distrib">twisted.web.distrib</a></code> to deliver HTTP requests to other
398
730
  programs for rendering, allowing subtrees of URL space to be delegated to
399
 
  multiple programs (on multiple machines).</li><li><code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.manhole.explorer.html" title="twisted.manhole.explorer">twisted.manhole.explorer</a></code> also uses
 
731
  multiple programs (on multiple machines).</li>
 
732
 
 
733
  <li><code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.manhole.explorer.html" title="twisted.manhole.explorer">twisted.manhole.explorer</a></code> also uses
400
734
  <code>Copyable</code> to distribute debugging information from the program
401
 
  under test to the debugging tool.</li></ul><h2>pb.Cacheable<a name="auto9"></a></h2><p>Sometimes the object you want to send to the remote process is big and
 
735
  under test to the debugging tool.</li>
 
736
  
 
737
</ul>
 
738
 
 
739
 
 
740
<h2>pb.Cacheable<a name="auto9"/></h2>
 
741
 
 
742
<p>Sometimes the object you want to send to the remote process is big and
402
743
slow. <q>big</q> means it takes a lot of data (storage, network bandwidth,
403
744
processing) to represent its state. <q>slow</q> means that state doesn't
404
745
change very frequently. It may be more efficient to send the full state only
405
746
once, the first time it is needed, then afterwards only send the differences
406
 
or changes in state whenever it is modified. The <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code> class provides a framework to
407
 
implement this.</p><p><code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code> is derived
408
 
from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>, so it is
 
747
or changes in state whenever it is modified. The <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code> class provides a framework to
 
748
implement this.</p>
 
749
 
 
750
<p><code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code> is derived
 
751
from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>, so it is
409
752
based upon the idea of an object's state being captured on the sending side,
410
753
and then turned into a new object on the receiving side. This is extended to
411
 
have an object <q>publishing</q> on the sending side (derived from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code>), matched with one
412
 
<q>observing</q> on the receiving side (derived from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.RemoteCache.html" title="twisted.spread.pb.RemoteCache">pb.RemoteCache</a></code>).</p><p>To effectively use <code>pb.Cacheable</code>, you need to isolate changes
 
754
have an object <q>publishing</q> on the sending side (derived from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code>), matched with one
 
755
<q>observing</q> on the receiving side (derived from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.RemoteCache.html" title="twisted.spread.pb.RemoteCache">pb.RemoteCache</a></code>).</p>
 
756
 
 
757
<p>To effectively use <code>pb.Cacheable</code>, you need to isolate changes
413
758
to your object into accessor functions (specifically <q>setter</q>
414
759
functions). Your object needs to get control <em>every</em> single time some
415
 
attribute is changed<a href="#footnote-3" title="of course you could be clever and add a hook to __setattr__, along with magical change-announcing subclasses of the usual builtin types, to detect changes that result from normal = set operations. The semi-magical property attributes that were introduced in Python-2.2 could be useful too. The result might be hard to maintain or extend, though."><super>3</super></a>.</p><p>You derive your sender-side class from <code>pb.Cacheable</code>, and you
416
 
add two methods: <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.Cacheable.getStateToCacheAndObserveFor.html" title="twisted.spread.flavors.Cacheable.getStateToCacheAndObserveFor">getStateToCacheAndObserveFor</a></code>
417
 
and <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.Cacheable.stoppedObserving.html" title="twisted.spread.flavors.Cacheable.stoppedObserving">stoppedObserving</a></code>. The first
 
760
attribute is changed<a href="#footnote-3" title="of course you could be clever and add a hook to __setattr__, along with magical change-announcing subclasses of the usual builtin types, to detect changes that result from normal = set operations. The semi-magical property attributes that were introduced in Python-2.2 could be useful too. The result might be hard to maintain or extend, though."><super>3</super></a>.</p>
 
761
 
 
762
<p>You derive your sender-side class from <code>pb.Cacheable</code>, and you
 
763
add two methods: <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.Cacheable.getStateToCacheAndObserveFor.html" title="twisted.spread.flavors.Cacheable.getStateToCacheAndObserveFor">getStateToCacheAndObserveFor</a></code>
 
764
and <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.Cacheable.stoppedObserving.html" title="twisted.spread.flavors.Cacheable.stoppedObserving">stoppedObserving</a></code>. The first
418
765
is called when a remote caching reference is first created, and retrieves
419
766
the data with which the cache is first filled. It also provides an
420
 
object called the <q>observer</q><!--
421
 
 
422
 
--><a href="#footnote-4" title="this is actually a RemoteCacheObserver, but it isn't very useful to subclass or modify, so simply treat it as a little demon that sits in your pb.Cacheable class and helps you distribute change notifications. The only useful thing to do with it is to run its callRemote method, which acts just like a normal pb.Referenceable's method of the same name."><super>4</super></a>
 
767
object called the <q>observer</q><a href="#footnote-4" title="this is actually a RemoteCacheObserver, but it isn't very useful to subclass or modify, so simply treat it as a little demon that sits in your pb.Cacheable class and helps you distribute change notifications. The only useful thing to do with it is to run its callRemote method, which acts just like a normal pb.Referenceable's method of the same name."><super>4</super></a>
423
768
 
424
769
that points at that receiver-side cache. Every time the state of the object
425
770
is changed, you give a message to the observer, informing them of the
426
771
change. The other method, <code>stoppedObserving</code>, is called when the
427
 
remote cache goes away, so that you can stop sending updates.</p><p>On the receiver end, you make your cache class inherit from <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.RemoteCache.html" title="twisted.spread.pb.RemoteCache">pb.RemoteCache</a></code>, and implement the
 
772
remote cache goes away, so that you can stop sending updates.</p>
 
773
 
 
774
<p>On the receiver end, you make your cache class inherit from <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.RemoteCache.html" title="twisted.spread.pb.RemoteCache">pb.RemoteCache</a></code>, and implement the
428
775
<code>setCopyableState</code> as you would for a <code>pb.RemoteCopy</code>
429
776
object. In addition, you must implement methods to receive the updates sent
430
777
to the observer by the <code>pb.Cacheable</code>: these methods should have
431
778
names that start with <code>observe_</code>, and match the
432
779
<code>callRemote</code> invocations from the sender side just as the usual
433
780
<code>remote_*</code> and <code>perspective_*</code> methods match normal
434
 
<code>callRemote</code> calls. </p><p>The first time a reference to the <code>pb.Cacheable</code> object is
 
781
<code>callRemote</code> calls. </p>
 
782
 
 
783
<p>The first time a reference to the <code>pb.Cacheable</code> object is
435
784
sent to any particular recipient, a sender-side Observer will be created for
436
785
it, and the <code>getStateToCacheAndObserveFor</code> method will be called
437
786
to get the current state and register the Observer. The state which that
438
787
returns is sent to the remote end and turned into a local representation
439
788
using <code>setCopyableState</code> just like <code>pb.RemoteCopy</code>,
440
 
described above (in fact it inherits from that class). </p><p>After that, your <q>setter</q> functions on the sender side should call
 
789
described above (in fact it inherits from that class). </p>
 
790
 
 
791
<p>After that, your <q>setter</q> functions on the sender side should call
441
792
<code>callRemote</code> on the Observer, which causes <code>observe_*</code>
442
793
methods to run on the receiver, which are then supposed to update the
443
 
receiver-local (cached) state.</p><p>When the receiver stops following the cached object and the last
 
794
receiver-local (cached) state.</p>
 
795
 
 
796
<p>When the receiver stops following the cached object and the last
444
797
reference goes away, the <code>pb.RemoteCache</code> object can be freed.
445
798
Just before it dies, it tells the sender side it no longer cares about the
446
799
original object. When <em>that</em> reference count goes to zero, the
447
800
Observer goes away and the <code>pb.Cacheable</code> object can stop
448
 
announcing every change that takes place. The <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.Cacheable.stoppedObserving.html" title="twisted.spread.flavors.Cacheable.stoppedObserving">stoppedObserving</a></code> method is
 
801
announcing every change that takes place. The <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.Cacheable.stoppedObserving.html" title="twisted.spread.flavors.Cacheable.stoppedObserving">stoppedObserving</a></code> method is
449
802
used to tell the <code>pb.Cacheable</code> that the Observer has gone
450
 
away.</p><p>With the <code>pb.Cacheable</code> and <code>pb.RemoteCache</code>
 
803
away.</p>
 
804
 
 
805
<p>With the <code>pb.Cacheable</code> and <code>pb.RemoteCache</code>
451
806
classes in place, bound together by a call to
452
807
<code>pb.setUnjellyableForClass</code>, all that remains is to pass a
453
808
reference to your <code>pb.Cacheable</code> over the wire to the remote end.
454
809
The corresponding <code>pb.RemoteCache</code> object will automatically be
455
810
created, and the matching methods will be used to keep the receiver-side
456
 
slave object in sync with the sender-side master object.</p><h3>Example<a name="auto10"></a></h3><p>Here is a complete example, in which the <code>MasterDuckPond</code> is
 
811
slave object in sync with the sender-side master object.</p>
 
812
 
 
813
<h3>Example<a name="auto10"/></h3>
 
814
 
 
815
<p>Here is a complete example, in which the <code>MasterDuckPond</code> is
457
816
controlled by the sending side, and the <code>SlaveDuckPond</code> is a
458
 
cache that tracks changes to the master:</p><div class="py-listing"><pre>
459
 
<span class="py-src-comment">#! /usr/bin/python
460
 
</span>
 
817
cache that tracks changes to the master:</p>
 
818
 
 
819
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
820
 2
 
821
 3
 
822
 4
 
823
 5
 
824
 6
 
825
 7
 
826
 8
 
827
 9
 
828
10
 
829
11
 
830
12
 
831
13
 
832
14
 
833
15
 
834
16
 
835
17
 
836
18
 
837
19
 
838
20
 
839
21
 
840
22
 
841
23
 
842
24
 
843
25
 
844
26
 
845
27
 
846
28
 
847
29
 
848
30
 
849
31
 
850
32
 
851
33
 
852
34
 
853
35
 
854
36
 
855
37
 
856
38
 
857
39
 
858
40
 
859
41
 
860
42
 
861
43
 
862
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
863
 
 
864
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
865
<span class="py-src-comment"># See LICENSE for details.</span>
 
866
 
461
867
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
462
868
 
463
869
<span class="py-src-keyword">class</span> <span class="py-src-identifier">MasterDuckPond</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">Cacheable</span>):
474
880
        <span class="py-src-keyword">for</span> <span class="py-src-variable">o</span> <span class="py-src-keyword">in</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">observers</span>: <span class="py-src-variable">o</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">'removeDuck'</span>, <span class="py-src-variable">duck</span>)
475
881
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">getStateToCacheAndObserveFor</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">perspective</span>, <span class="py-src-parameter">observer</span>):
476
882
        <span class="py-src-variable">self</span>.<span class="py-src-variable">observers</span>.<span class="py-src-variable">append</span>(<span class="py-src-variable">observer</span>)
477
 
        <span class="py-src-comment"># you should ignore pb.Cacheable-specific state, like self.observers
478
 
</span>        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">ducks</span> <span class="py-src-comment"># in this case, just a list of ducks</span>
 
883
        <span class="py-src-comment"># you should ignore pb.Cacheable-specific state, like self.observers</span>
 
884
        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">ducks</span> <span class="py-src-comment"># in this case, just a list of ducks</span>
479
885
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">stoppedObserving</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">perspective</span>, <span class="py-src-parameter">observer</span>):
480
886
        <span class="py-src-variable">self</span>.<span class="py-src-variable">observers</span>.<span class="py-src-variable">remove</span>(<span class="py-src-variable">observer</span>)
481
887
 
482
888
<span class="py-src-keyword">class</span> <span class="py-src-identifier">SlaveDuckPond</span>(<span class="py-src-parameter">pb</span>.<span class="py-src-parameter">RemoteCache</span>):
483
 
    <span class="py-src-comment"># This is a cache of a remote MasterDuckPond
484
 
</span>    <span class="py-src-keyword">def</span> <span class="py-src-identifier">count</span>(<span class="py-src-parameter">self</span>):
 
889
    <span class="py-src-comment"># This is a cache of a remote MasterDuckPond</span>
 
890
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">count</span>(<span class="py-src-parameter">self</span>):
485
891
        <span class="py-src-keyword">return</span> <span class="py-src-variable">len</span>(<span class="py-src-variable">self</span>.<span class="py-src-variable">cacheducks</span>)
486
892
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">getDucks</span>(<span class="py-src-parameter">self</span>):
487
893
        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">cacheducks</span>
496
902
        <span class="py-src-variable">self</span>.<span class="py-src-variable">cacheducks</span>.<span class="py-src-variable">remove</span>(<span class="py-src-variable">deadDuck</span>)
497
903
 
498
904
<span class="py-src-variable">pb</span>.<span class="py-src-variable">setUnjellyableForClass</span>(<span class="py-src-variable">MasterDuckPond</span>, <span class="py-src-variable">SlaveDuckPond</span>)
499
 
</pre><div class="caption">Source listing - <a href="listings/pb/cache_classes.py"><span class="filename">listings/pb/cache_classes.py</span></a></div></div><div class="py-listing"><pre>
500
 
<span class="py-src-comment">#! /usr/bin/python
501
 
</span>
 
905
</pre><div class="caption">Source listing - <a href="listings/pb/cache_classes.py"><span class="filename">listings/pb/cache_classes.py</span></a></div></div>
 
906
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
907
 2
 
908
 3
 
909
 4
 
910
 5
 
911
 6
 
912
 7
 
913
 8
 
914
 9
 
915
10
 
916
11
 
917
12
 
918
13
 
919
14
 
920
15
 
921
16
 
922
17
 
923
18
 
924
19
 
925
20
 
926
21
 
927
22
 
928
23
 
929
24
 
930
25
 
931
26
 
932
27
 
933
28
 
934
29
 
935
30
 
936
31
 
937
32
 
938
33
 
939
34
 
940
35
 
941
36
 
942
37
 
943
38
 
944
39
 
945
40
 
946
41
 
947
42
 
948
43
 
949
44
 
950
45
 
951
46
 
952
47
 
953
48
 
954
49
 
955
50
 
956
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
957
 
 
958
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
959
<span class="py-src-comment"># See LICENSE for details.</span>
 
960
 
502
961
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>, <span class="py-src-variable">jelly</span>
503
962
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span>
504
963
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
544
1003
 
545
1004
<span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> == <span class="py-src-string">'__main__'</span>:
546
1005
    <span class="py-src-variable">main</span>()
547
 
</pre><div class="caption">Source listing - <a href="listings/pb/cache_sender.py"><span class="filename">listings/pb/cache_sender.py</span></a></div></div><div class="py-listing"><pre>
548
 
<span class="py-src-comment">#! /usr/bin/python
549
 
</span>
 
1006
</pre><div class="caption">Source listing - <a href="listings/pb/cache_sender.py"><span class="filename">listings/pb/cache_sender.py</span></a></div></div>
 
1007
<div class="py-listing"><pre><p class="py-linenumber"> 1
 
1008
 2
 
1009
 3
 
1010
 4
 
1011
 5
 
1012
 6
 
1013
 7
 
1014
 8
 
1015
 9
 
1016
10
 
1017
11
 
1018
12
 
1019
13
 
1020
14
 
1021
15
 
1022
16
 
1023
17
 
1024
18
 
1025
19
 
1026
20
 
1027
21
 
1028
22
 
1029
23
 
1030
24
 
1031
25
 
1032
26
 
1033
27
 
1034
28
 
1035
</p><span class="py-src-comment">#!/usr/bin/env python</span>
 
1036
 
 
1037
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
 
1038
<span class="py-src-comment"># See LICENSE for details.</span>
 
1039
 
550
1040
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">application</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">service</span>, <span class="py-src-variable">internet</span>
551
1041
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span>
552
1042
<span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span>.<span class="py-src-variable">spread</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">pb</span>
560
1050
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_checkDucks</span>(<span class="py-src-parameter">self</span>):
561
1051
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;[%d] ducks: &quot;</span> % <span class="py-src-variable">self</span>.<span class="py-src-variable">pond</span>.<span class="py-src-variable">count</span>(), <span class="py-src-variable">self</span>.<span class="py-src-variable">pond</span>.<span class="py-src-variable">getDucks</span>()
562
1052
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_ignorePond</span>(<span class="py-src-parameter">self</span>):
563
 
        <span class="py-src-comment"># stop watching the pond
564
 
</span>        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;dropping pond&quot;</span>
565
 
        <span class="py-src-comment"># gc causes __del__ causes 'decache' msg causes stoppedObserving
566
 
</span>        <span class="py-src-variable">self</span>.<span class="py-src-variable">pond</span> = <span class="py-src-variable">None</span>
 
1053
        <span class="py-src-comment"># stop watching the pond</span>
 
1054
        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;dropping pond&quot;</span>
 
1055
        <span class="py-src-comment"># gc causes __del__ causes 'decache' msg causes stoppedObserving</span>
 
1056
        <span class="py-src-variable">self</span>.<span class="py-src-variable">pond</span> = <span class="py-src-variable">None</span>
567
1057
    <span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_shutdown</span>(<span class="py-src-parameter">self</span>):
568
1058
        <span class="py-src-variable">reactor</span>.<span class="py-src-variable">stop</span>()
569
1059
 
570
1060
<span class="py-src-variable">application</span> = <span class="py-src-variable">service</span>.<span class="py-src-variable">Application</span>(<span class="py-src-string">&quot;copy_receiver&quot;</span>)
571
1061
<span class="py-src-variable">internet</span>.<span class="py-src-variable">TCPServer</span>(<span class="py-src-number">8800</span>, <span class="py-src-variable">pb</span>.<span class="py-src-variable">PBServerFactory</span>(<span class="py-src-variable">Receiver</span>())).<span class="py-src-variable">setServiceParent</span>(
572
1062
    <span class="py-src-variable">service</span>.<span class="py-src-variable">IServiceCollection</span>(<span class="py-src-variable">application</span>))
573
 
</pre><div class="caption">Source listing - <a href="listings/pb/cache_receiver.py"><span class="filename">listings/pb/cache_receiver.py</span></a></div></div><p>When run, this example emits the following:</p><pre class="shell">
 
1063
</pre><div class="caption">Source listing - <a href="listings/pb/cache_receiver.py"><span class="filename">listings/pb/cache_receiver.py</span></a></div></div>
 
1064
<p>When run, this example emits the following:</p>
 
1065
 
 
1066
<pre class="shell" xml:space="preserve">
574
1067
% twistd -n -y cache_receiver.py 
575
1068
[-] twisted.spread.pb.PBServerFactory starting on 8800
576
1069
[-] Starting factory &lt;twisted.spread.pb.PBServerFactory instance at
585
1078
[Broker,0,127.0.0.1] [2] ducks:  ['two duck', 'ugly duckling']
586
1079
[Broker,0,127.0.0.1] dropping pond
587
1080
588
 
</pre><pre class="shell">
 
1081
</pre>
 
1082
 
 
1083
<pre class="shell" xml:space="preserve">
589
1084
% ./cache_sender.py 
590
1085
I have [2] ducks
591
1086
I have [3] ducks
592
1087
I have [2] ducks
593
1088
Main loop terminated.
594
1089
595
 
</pre><p>Points to notice:</p><ul><li>There is one <code>Observer</code> for each remote program that holds
 
1090
</pre>
 
1091
 
 
1092
 
 
1093
<p>Points to notice:</p>
 
1094
 
 
1095
<ul>
 
1096
  <li>There is one <code>Observer</code> for each remote program that holds
596
1097
  an active reference. Multiple references inside the same program don't
597
1098
  matter: the serialization layer notices the duplicates and does the
598
1099
  appropriate reference counting<a href="#footnote-5" title="this applies to multiple references through the same Broker. If you've managed to make multiple TCP connections to the same program, you deserve whatever you get."><super>5</super></a>.
599
 
  </li><li>Multiple Observers need to be kept in a list, and all of them need to
 
1100
  </li>
 
1101
  
 
1102
  <li>Multiple Observers need to be kept in a list, and all of them need to
600
1103
  be updated when something changes. By sending the initial state at the
601
1104
  same time as you add the observer to the list, in a single atomic action
602
1105
  that cannot be interrupted by a state change, you insure that you can send
603
 
  the same status update to all the observers.</li><li>The <code>observer.callRemote</code> calls can still fail. If the
 
1106
  the same status update to all the observers.</li>
 
1107
 
 
1108
  <li>The <code>observer.callRemote</code> calls can still fail. If the
604
1109
  remote side has disconnected very recently and
605
1110
  <code>stoppedObserving</code> has not yet been called, you may get a
606
1111
  <code>DeadReferenceError</code>. It is a good idea to add an errback to
607
1112
  those <code>callRemote</code>s to throw away such an error. This is a
608
1113
  useful idiom:
609
1114
 
610
 
  <pre class="python">
611
 
<span class="py-src-variable">observer</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">'foo'</span>, <span class="py-src-variable">arg</span>).<span class="py-src-variable">addErrback</span>(<span class="py-src-keyword">lambda</span> <span class="py-src-variable">f</span>: <span class="py-src-variable">None</span>)
 
1115
  <pre class="python"><p class="py-linenumber">1
 
1116
</p><span class="py-src-variable">observer</span>.<span class="py-src-variable">callRemote</span>(<span class="py-src-string">'foo'</span>, <span class="py-src-variable">arg</span>).<span class="py-src-variable">addErrback</span>(<span class="py-src-keyword">lambda</span> <span class="py-src-variable">f</span>: <span class="py-src-variable">None</span>)
612
1117
</pre>
613
1118
 
614
 
  (XXX: verify that this is actually a concern)</li><li><code>getStateToCacheAndObserverFor</code> must return some object
 
1119
  (XXX: verify that this is actually a concern)</li>
 
1120
 
 
1121
  <li><code>getStateToCacheAndObserverFor</code> must return some object
615
1122
  that represents the current state of the object. This may simply be the
616
1123
  object's <code>__dict__</code> attribute. It is a good idea to remove the
617
1124
  <code>pb.Cacheable</code>-specific members of it before sending it to the
618
1125
  remote end. The list of Observers, in particular, should be left out, to
619
1126
  avoid dizzying recursive Cacheable references. The mind boggles as to the
620
 
  potential consequences of leaving in such an item.</li><li>A <code>perspective</code> argument is available to
 
1127
  potential consequences of leaving in such an item.</li>
 
1128
 
 
1129
  <li>A <code>perspective</code> argument is available to
621
1130
  <code>getStateToCacheAndObserveFor</code>, as well as
622
1131
  <code>stoppedObserving</code>. I think the purpose of this is to allow
623
1132
  viewer-specific changes to the way the cache is updated. If all remote
624
 
  viewers are supposed to see the same data, it can be ignored.</li></ul><p>XXX: understand, then explain use of varying cached state depending upon
625
 
perspective.</p><h3>More Information<a name="auto11"></a></h3><ul><li>The best source for information comes from the docstrings in <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.html" title="twisted.spread.flavors">twisted.spread.flavors</a></code>, where <code>pb.Cacheable</code>
626
 
  is implemented.</li><li><code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.manhole.explorer.html" title="twisted.manhole.explorer">twisted.manhole.explorer</a></code> uses
 
1133
  viewers are supposed to see the same data, it can be ignored.</li>
 
1134
  
 
1135
</ul>
 
1136
 
 
1137
 
 
1138
<p>XXX: understand, then explain use of varying cached state depending upon
 
1139
perspective.</p>
 
1140
 
 
1141
<h3>More Information<a name="auto11"/></h3>
 
1142
 
 
1143
<ul>
 
1144
  <li>The best source for information comes from the docstrings in <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.html" title="twisted.spread.flavors">twisted.spread.flavors</a></code>, where <code>pb.Cacheable</code>
 
1145
  is implemented.</li>
 
1146
 
 
1147
  <li><code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.manhole.explorer.html" title="twisted.manhole.explorer">twisted.manhole.explorer</a></code> uses
627
1148
  <code>Cacheable</code>, and does some fairly interesting things with it.
628
1149
  (XXX: I've heard explorer is currently broken, it might not be a good
629
 
  example to recommend)</li><li>The <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.publish.html" title="twisted.spread.publish">spread.publish</a></code> module also
 
1150
  example to recommend)</li>
 
1151
 
 
1152
  <li>The <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.publish.html" title="twisted.spread.publish">spread.publish</a></code> module also
630
1153
  uses <code>Cacheable</code>, and might be a source of further
631
 
  information.</li></ul><h2>Footnotes</h2><ol><li><a name="footnote-1"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote"><p>Note that, in this context, <q>unjelly</q> is
 
1154
  information.</li>
 
1155
</ul>
 
1156
 
 
1157
 
 
1158
 
 
1159
<h2>Footnotes</h2><ol><li><a name="footnote-1"><span class="footnote"> <p>Note that, in this context, <q>unjelly</q> is
632
1160
a verb with the opposite meaning of <q>jelly</q>. The verb <q>to jelly</q>
633
1161
means to serialize an object or data structure into a sequence of bytes (or
634
1162
other primitive transmittable/storable representation), while <q>to
639
1167
that a serialized representation A (of some remote object) can be
640
1168
unserialized into a local object of type B. It is these objects <q>B</q>
641
1169
that are the <q>Unjellyable</q> second argument of the
642
 
<code>setUnjellyableForClass</code> function.</p><p>In particular, <q>unjellyable</q> does <em>not</em> mean <q>cannot be
643
 
jellied</q>. <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.jelly.Unpersistable.html" title="twisted.spread.jelly.Unpersistable">Unpersistable</a></code> means <q>not
 
1170
<code>setUnjellyableForClass</code> function.</p>
 
1171
 
 
1172
<p>In particular, <q>unjellyable</q> does <em>not</em> mean <q>cannot be
 
1173
jellied</q>. <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.jelly.Unpersistable.html" title="twisted.spread.jelly.Unpersistable">Unpersistable</a></code> means <q>not
644
1174
persistable</q>, but <q>unjelly</q>, <q>unserialize</q>, and <q>unpickle</q>
645
1175
mean to reverse the operations of <q>jellying</q>, <q>serializing</q>, and
646
 
<q>pickling</q>.</p></span></a></li><li><a name="footnote-2"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote"><code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code> is actually defined
647
 
  as <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.RemoteCopy.html" title="twisted.spread.flavors.RemoteCopy">flavors.RemoteCopy</a></code>, but
648
 
  <code>pb.RemoteCopy</code> is the preferred way to access it</span></a></li><li><a name="footnote-3"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote">of course you could be clever and
 
1176
<q>pickling</q>.</p> </span></a></li><li><a name="footnote-2"><span class="footnote"><code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code> is actually defined
 
1177
  as <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.RemoteCopy.html" title="twisted.spread.flavors.RemoteCopy">flavors.RemoteCopy</a></code>, but
 
1178
  <code>pb.RemoteCopy</code> is the preferred way to access it</span></a></li><li><a name="footnote-3"><span class="footnote">of course you could be clever and
649
1179
add a hook to <code>__setattr__</code>, along with magical change-announcing
650
1180
subclasses of the usual builtin types, to detect changes that result from
651
1181
normal <q>=</q> set operations. The semi-magical <q>property attributes</q>
652
1182
that were introduced in Python-2.2 could be useful too. The result might be
653
 
hard to maintain or extend, though.</span></a></li><li><a name="footnote-4"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote"> this is actually a <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.flavors.RemoteCacheObserver.html" title="twisted.spread.flavors.RemoteCacheObserver">RemoteCacheObserver</a></code>, but it isn't very
 
1183
hard to maintain or extend, though.</span></a></li><li><a name="footnote-4"><span class="footnote"> this is actually a <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.flavors.RemoteCacheObserver.html" title="twisted.spread.flavors.RemoteCacheObserver">RemoteCacheObserver</a></code>, but it isn't very
654
1184
useful to subclass or modify, so simply treat it as a little demon that sits
655
1185
in your <code>pb.Cacheable</code> class and helps you distribute change
656
1186
notifications. The only useful thing to do with it is to run its
657
1187
<code>callRemote</code> method, which acts just like a normal
658
 
<code>pb.Referenceable</code>'s method of the same name.</span></a></li><li><a name="footnote-5"><span xmlns="http://www.w3.org/1999/xhtml" class="footnote">this applies to
659
 
  multiple references through the same <code class="API"><a href="http://twistedmatrix.com/documents/8.2.0/api/twisted.spread.pb.Broker.html" title="twisted.spread.pb.Broker">Broker</a></code>. If you've managed to make multiple
660
 
  TCP connections to the same program, you deserve whatever you get.</span></a></li></ol></div><p><a href="index.html">Index</a></p><span class="version">Version: 8.2.0</span></body></html>
 
 
b'\\ No newline at end of file'
 
1188
<code>pb.Referenceable</code>'s method of the same name.</span></a></li><li><a name="footnote-5"><span class="footnote">this applies to
 
1189
  multiple references through the same <code class="API"><a href="http://twistedmatrix.com/documents/9.0.0/api/twisted.spread.pb.Broker.html" title="twisted.spread.pb.Broker">Broker</a></code>. If you've managed to make multiple
 
1190
  TCP connections to the same program, you deserve whatever you get.</span></a></li></ol></div>
 
1191
 
 
1192
    <p><a href="index.html">Index</a></p>
 
1193
    <span class="version">Version: 9.0.0</span>
 
1194
  </body>
 
1195
</html>
 
 
b'\\ No newline at end of file'