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"?>
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">
7
<title>Twisted Documentation: PB Copyable: Passing Complex Types</title>
8
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
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>
17
<h2>Overview<a name="auto0"/></h2>
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>
24
<h2>Motivation<a name="auto1"/></h2>
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
36
<h2>Passing Objects<a name="auto2"/></h2>
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>
42
<pre class="python"><p class="py-linenumber">1
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>
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">"sendPond"</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
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>
63
<h3>Security Options<a name="auto3"/></h3>
65
<p>What's the big deal? What's wrong with just copying a class into another
66
process' namespace?</p>
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>
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>
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("~/.gnupg/secring.gpg")</code>. Or an instance of
42
<code>telnetlib.Telnet("localhost", "chargen")</code>. </p><p>Classes you've written for your own program are likely to have far more
86
<code>telnetlib.Telnet("localhost", "chargen")</code>. </p>
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
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>
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>
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>
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">
403
<p>When this is run, it produces the following output:</p>
405
<pre class="shell" xml:space="preserve">
227
406
[-] twisted.spread.pb.PBServerFactory starting on 8800
228
407
[-] Starting factory <twisted.spread.pb.PBServerFactory instance at
230
409
[Broker,0,127.0.0.1] got pond: <__builtin__.ReceiverPond instance at
232
411
[Broker,0,127.0.0.1] 7 frogs
233
</pre><pre class="shell">
414
<pre class="shell" xml:space="preserve">
234
415
% ./copy_sender.py
236
417
copy_sender.CopyPond
237
418
pond arrived safe and sound
238
419
Main loop terminated.
240
</pre><h3>Controlling the Copied State<a name="auto6"></a></h3><p>By overriding <code>getStateToCopy</code> and
425
<h3>Controlling the Copied State<a name="auto6"/></h3>
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>
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
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
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>
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>
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>
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>
722
<h3>More Information<a name="auto8"/></h3>
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>
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>
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>
740
<h2>pb.Cacheable<a name="auto9"/></h2>
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
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>
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>
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><!--
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>
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>
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>
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>
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>
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>
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>
813
<h3>Example<a name="auto10"/></h3>
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
817
cache that tracks changes to the master:</p>
819
<div class="py-listing"><pre><p class="py-linenumber"> 1
862
</p><span class="py-src-comment">#!/usr/bin/env python</span>
864
<span class="py-src-comment"># Copyright (c) 2009 Twisted Matrix Laboratories.</span>
865
<span class="py-src-comment"># See LICENSE for details.</span>
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>
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>):
585
1078
[Broker,0,127.0.0.1] [2] ducks: ['two duck', 'ugly duckling']
586
1079
[Broker,0,127.0.0.1] dropping pond
588
</pre><pre class="shell">
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.
595
</pre><p>Points to notice:</p><ul><li>There is one <code>Observer</code> for each remote program that holds
1093
<p>Points to notice:</p>
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
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>
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
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>)
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>
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>
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>
1138
<p>XXX: understand, then explain use of varying cached state depending upon
1141
<h3>More Information<a name="auto11"/></h3>
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>
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>
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
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>
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>
1192
<p><a href="index.html">Index</a></p>
1193
<span class="version">Version: 9.0.0</span>
b'\\ No newline at end of file'