pb-copyable.html   [plain text]


<?xml version="1.0"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "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="../howto/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
class instances) to and from a remote process. The first section is on
simply copying the contents of an object to a remote process (<code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>). The second covers how
to copy those contents once, then update them later when they change (<code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.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
pass basic types to a remote process, by using them in the arguments or
return values of a <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Referenceable.callRemote.html" title="twisted.spread.pb.Referenceable.callRemote">callRemote</a></code> function. However,
if you've experimented with it, you may have discovered problems when trying
to pass anything more complicated than a primitive int/list/dict/string
type, or another <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Referenceable.html" title="twisted.spread.pb.Referenceable">pb.Referenceable</a></code> object. At some point you want
to pass entire objects between processes, instead of having to reduce them
down to dictionaries on one end and then re-instantiating them on the
other.</p><h2>Passing Objects<a name="auto2"></a></h2><p>The most obvious and straightforward way to send an object to a remote
process is with something like the following code. It also happens that this
code doesn't work, as will be explained below.</p><pre class="python">
<span class="py-src-keyword">class</span> <span class="py-src-identifier">LilyPond</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">  </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">frogs</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">frogs</span> <span class="py-src-op">=</span> <span class="py-src-variable">frogs</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">LilyPond</span><span class="py-src-op">(</span><span class="py-src-number">12</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-variable">ref</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;sendPond&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">pond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></pre><p>If you try to run this, you might hope that a suitable remote end which
implements the <code>remote_sendPond</code> method would see that method get
invoked with an instance from the <code>LilyPond</code> class. But instead,
you'll encounter the dreaded <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.jelly.InsecureJelly.html" title="twisted.spread.jelly.InsecureJelly">InsecureJelly</a></code> exception. This is
Twisted's way of telling you that you've violated a security restriction,
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
process' namespace?</p><p>Reversing the question might make it easier to see the issue: what is the
problem with accepting a stranger's request to create an arbitrary object in
your local namespace? The real question is how much power you are granting
them: what actions can they convince you to take on the basis of the bytes
they are sending you over that remote connection.</p><p>Objects generally represent more power than basic types like strings and
dictionaries because they also contain (or reference) code, which can modify
other data structures when executed. Once previously-trusted data is
subverted, the rest of the program is compromised.</p><p>The built-in Python <q>batteries included</q> classes are relatively
tame, but you still wouldn't want to let a foreign program use them to
create arbitrary objects in your namespace or on your computer. Imagine a
protocol that involved sending a file-like object with a <code>read()</code>
method that was supposed to used later to retrieve a document. Then imagine
what if that object were created with
<code>os.fdopen(&quot;~/.gnupg/secring.gpg&quot;)</code>. Or an instance of
<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
power. They may run code during <code>__init__</code>, or even have special
meaning simply because of their existence. A program might have
<code>User</code> objects to represent user accounts, and have a rule that
says all <code>User</code> objects in the system are referenced when
authorizing a login session. (In this system, <code>User.__init__</code>
would probably add the object to a global list of known users). The simple
act of creating an object would give access to somebody. If you could be
tricked into creating a bad object, an unauthorized user would get
access.</p><p>So object creation needs to be part of a system's security design. The
dotted line between <q>trusted inside</q> and <q>untrusted outside</q> needs
to describe what may be done in response to outside events. One of those
events is the receipt of an object through a PB remote procedure call, which
is a request to create an object in your <q>inside</q> namespace. The
question is what to do in response to it. For this reason, you must
explicitly specific what remote classes will be accepted, and how their
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
incoming serialized object is: what class should we create? The simplistic
answer is to create the <q>same kind</q> that was serialized on the sender's
end of the wire, but this is not as easy or as straightforward as you might
think. Remember that the request is coming from a different program, using a
potentially different set of class libraries. In fact, since PB has also
been implemented in Java, Emacs-Lisp, and other languages, there's no
guarantee that the sender is even running Python! All we know on the
receiving end is a list of two things which describe the instance they are
trying to send us: the name of the class, and a representation of the
contents of the object.</p><p>PB lets you specify the mapping from remote class names to local classes
with the <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.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>.


This function takes a remote/sender class reference (either the
fully-qualified name as used by the sending end, or a class object from
which the name can be extracted), and a local/recipient class (used to
create the local representation for incoming serialized objects). Whenever
the remote end sends an object, the class name that they transmit is looked
up in the table controlled by this function. If a matching class is found,
it is used to create the local object. If not, you get the
<code>InsecureJelly</code> exception.</p><p>In general you expect both ends to share the same codebase: either you
control the program that is running on both ends of the wire, or both
programs share some kind of common language that is implemented in code
which exists on both ends. You wouldn't expect them to send you an object of
the MyFooziWhatZit class unless you also had a definition for that class. So
it is reasonable for the Jelly layer to reject all incoming classes except
the ones that you have explicitly marked with
<code>setUnjellyableForClass</code>. But keep in mind that the sender's idea
of a <code>User</code> object might differ from the recipient's, either
through namespace collisions between unrelated packages, version skew
between nodes that haven't been updated at the same rate, or a malicious
intruder trying to cause your code to fail in some interesting or
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
one side to the other?</p><div class="py-listing"><pre>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-op">,</span> <span class="py-src-variable">jelly</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">LilyPond</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">setStuff</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">color</span><span class="py-src-op">,</span> <span class="py-src-parameter">numFrogs</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">color</span> <span class="py-src-op">=</span> <span class="py-src-variable">color</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">numFrogs</span> <span class="py-src-op">=</span> <span class="py-src-variable">numFrogs</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">countFrogs</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;%d frogs&quot;</span> <span class="py-src-op">%</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">numFrogs</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">class</span> <span class="py-src-identifier">CopyPond</span><span class="py-src-op">(</span><span class="py-src-parameter">LilyPond</span><span class="py-src-op">,</span> <span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">Copyable</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">pass</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-keyword">class</span> <span class="py-src-identifier">Sender</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">pond</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">pond</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">got_obj</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">remote</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">remote</span> <span class="py-src-op">=</span> <span class="py-src-variable">remote</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span> <span class="py-src-op">=</span> <span class="py-src-variable">remote</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;takePond&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">ok</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">addErrback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">notOk</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">ok</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">response</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;pond arrived&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">response</span><span class="py-src-newline">
</span>        <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">notOk</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">failure</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;error during takePond:&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">if</span> <span class="py-src-variable">failure</span><span class="py-src-op">.</span><span class="py-src-variable">type</span> <span class="py-src-op">==</span> <span class="py-src-variable">jelly</span><span class="py-src-op">.</span><span class="py-src-variable">InsecureJelly</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">            </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot; InsecureJelly&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-dedent"></span><span class="py-src-keyword">else</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">            </span><span class="py-src-keyword">print</span> <span class="py-src-variable">failure</span><span class="py-src-newline">
</span>        <span class="py-src-dedent"></span><span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">return</span> <span class="py-src-variable">None</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><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">CopyPond</span>  <span class="py-src-comment"># so it's not __main__.CopyPond</span><span class="py-src-newline">
</span>    <span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">CopyPond</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">setStuff</span><span class="py-src-op">(</span><span class="py-src-string">&quot;green&quot;</span><span class="py-src-op">,</span> <span class="py-src-number">7</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">countFrogs</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-comment"># class name:
</span>    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;.&quot;</span><span class="py-src-op">.</span><span class="py-src-variable">join</span><span class="py-src-op">(</span><span class="py-src-op">[</span><span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">__class__</span><span class="py-src-op">.</span><span class="py-src-variable">__module__</span><span class="py-src-op">,</span> <span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">__class__</span><span class="py-src-op">.</span><span class="py-src-variable">__name__</span><span class="py-src-op">]</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-variable">sender</span> <span class="py-src-op">=</span> <span class="py-src-variable">Sender</span><span class="py-src-op">(</span><span class="py-src-variable">pond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">factory</span> <span class="py-src-op">=</span> <span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">PBClientFactory</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">connectTCP</span><span class="py-src-op">(</span><span class="py-src-string">&quot;localhost&quot;</span><span class="py-src-op">,</span> <span class="py-src-number">8800</span><span class="py-src-op">,</span> <span class="py-src-variable">factory</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">deferred</span> <span class="py-src-op">=</span> <span class="py-src-variable">factory</span><span class="py-src-op">.</span><span class="py-src-variable">getRootObject</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">deferred</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">sender</span><span class="py-src-op">.</span><span class="py-src-variable">got_obj</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">run</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> <span class="py-src-op">==</span> <span class="py-src-string">'__main__'</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-variable">main</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-dedent"></span><span class="py-src-endmarker"></span></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>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-op">,</span> <span class="py-src-variable">internet</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-newline">
</span><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-op">,</span> <span class="py-src-variable">CopyPond</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span><span class="py-src-newline">
</span><span class="py-src-keyword">import</span> <span class="py-src-variable">sys</span><span class="py-src-newline">
</span><span class="py-src-comment">#log.startLogging(sys.stdout)
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">ReceiverPond</span><span class="py-src-op">(</span><span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">RemoteCopy</span><span class="py-src-op">,</span> <span class="py-src-parameter">LilyPond</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">pass</span><span class="py-src-newline">
</span><span class="py-src-dedent"></span><span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">setUnjellyableForClass</span><span class="py-src-op">(</span><span class="py-src-variable">CopyPond</span><span class="py-src-op">,</span> <span class="py-src-variable">ReceiverPond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">Receiver</span><span class="py-src-op">(</span><span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">Root</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_takePond</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">pond</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot; got pond:&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">pond</span><span class="py-src-newline">
</span>        <span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">countFrogs</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">return</span> <span class="py-src-string">&quot;safe and sound&quot;</span> <span class="py-src-comment"># positive acknowledgement</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_shutdown</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-variable">application</span> <span class="py-src-op">=</span> <span class="py-src-variable">service</span><span class="py-src-op">.</span><span class="py-src-variable">Application</span><span class="py-src-op">(</span><span class="py-src-string">&quot;copy_receiver&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-variable">internet</span><span class="py-src-op">.</span><span class="py-src-variable">TCPServer</span><span class="py-src-op">(</span><span class="py-src-number">8800</span><span class="py-src-op">,</span> <span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">PBServerFactory</span><span class="py-src-op">(</span><span class="py-src-variable">Receiver</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">setServiceParent</span><span class="py-src-op">(</span><span class="py-src-nl">
</span>    <span class="py-src-variable">service</span><span class="py-src-op">.</span><span class="py-src-variable">IServiceCollection</span><span class="py-src-op">(</span><span class="py-src-variable">application</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></pre><div class="caption">Source listing - <a href="listings/pb/copy_receiver.py"><span class="filename">listings/pb/copy_receiver.py</span></a></div></div><p>The sending side has a class called <code>LilyPond</code>. To make this
eligble for transport through <code>callRemote</code> (either as an
argument, a return value, or something referenced by either of those [like a
dictionary value]), it must inherit from one of the four <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.Serializable.html" title="twisted.spread.flavors.Serializable">Serializable</a></code> classes. In this section,
we focus on <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.Copyable.html" title="twisted.spread.flavors.Copyable">Copyable</a></code>.
The copyable subclass of <code>LilyPond</code> is called
<code>CopyPond</code>. We create an instance of it and send it through
<code>callRemote</code> as an argument to the receiver's
<code>remote_takePond</code> method. The Jelly layer will serialize
(<q>jelly</q>) that object as an instance with a class name of
<q>copy_sender.CopyPond</q> and some chunk of data that represents the
object's state. <code>pond.__class__.__module__</code> and
<code>pond.__class__.__name__</code> are used to derive the class name
string. The object's <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.flavors.Copyable.getStateToCopy.html" title="twisted.spread.pb.flavors.Copyable.getStateToCopy">getStateToCopy</a></code> method is
used to get the state: this is provided by <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>, and the default just retrieves
<code>self.__dict__</code>. This works just like the optional
<code>__getstate__</code> method used by <code>pickle</code>. The pair of
name and state are sent over the wire to the receiver.</p><p>The receiving end defines a local class named <code>ReceiverPond</code>
to represent incoming <code>LilyPond</code> instances. This class derives
from the sender's <code>LilyPond</code> class (with a fully-qualified name
of <code>copy_sender.LilyPond</code>), which specifies how we expect it to
behave. We trust that this is the same <code>LilyPond</code> class as the
sender used. (At the very least, we hope ours will be able to accept a state
created by theirs). It also inherits from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code>, which is a requirement for all
classes that act in this local-representative role (those which are given to
the second argument of <code>setUnjellyableForClass</code>).
<code>RemoteCopy</code> provides the methods that tell the Jelly layer how
to create the local object from the incoming serialized state.</p><p>Then <code>setUnjellyableForClass</code> is used to register the two
classes. This has two effects: instances of the remote class (the first
argument) will be allowed in through the security layer, and instances of
the local class (the second argument) will be used to contain the state that
is transmitted when the sender serializes the remote object.</p><p>When the receiver unserializes (<q>unjellies</q>) the object, it will
create an instance of the local <code>ReceiverPond</code> class, and hand
the transmitted state (usually in the form of a dictionary) to that object's
<code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.flavors.RemoteCopy.setCopyableState.html" title="twisted.spread.pb.flavors.RemoteCopy.setCopyableState">setCopyableState</a></code> method.
This acts just like the <code>__setstate__</code> method that
<code>pickle</code> uses when unserializing an object.
<code>getStateToCopy</code>/<code>setCopyableState</code> are distinct from
<code>__getstate__</code>/<code>__setstate__</code> to allow objects to be
persisted (across time) differently than they are transmitted (across
[memory]space).</p><p>When this is run, it produces the following output:</p><pre class="shell">
[-] twisted.spread.pb.PBServerFactory starting on 8800
[-] Starting factory &lt;twisted.spread.pb.PBServerFactory instance at
0x406159cc&gt;
[Broker,0,127.0.0.1]  got pond: &lt;__builtin__.ReceiverPond instance at
0x406ec5ec&gt;
[Broker,0,127.0.0.1] 7 frogs
</pre><pre class="shell">
% ./copy_sender.py 
7 frogs
copy_sender.CopyPond
pond arrived safe and sound
Main loop terminated.
%
</pre><h3>Controlling the Copied State<a name="auto6"></a></h3><p>By overriding <code>getStateToCopy</code> and
<code>setCopyableState</code>, you can control how the object is transmitted
over the wire. For example, you might want perform some data-reduction:
pre-compute some results instead of sending all the raw data over the wire.
Or you could replace references to a local object on the sender's side with
markers before sending, then upon receipt replace those markers with
references to a receiver-side proxy that could perform the same operations
against a local cache of data.</p><p>Another good use for <code>getStateToCopy</code> is to implement
<q>local-only</q> attributes: data that is only accessible by the local
process, not to any remote users. For example, a <code>.password</code>
attribute could be removed from the object state before sending to a remote
system. Combined with the fact that <code>Copyable</code> objects return
unchanged from a round trip, this could be used to build a
challenge-response system (in fact PB does this with
<code>pb.Referenceable</code> objects to implement authorization as
described <a href="pb-cred.html">here</a>).</p><p>Whatever <code>getStateToCopy</code> returns from the sending object will
be serialized and sent over the wire; <code>setCopyableState</code> gets
whatever comes over the wire and is responsible for setting up the state of
the object it lives in.</p><div class="py-listing"><pre>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">FrogPond</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">numFrogs</span><span class="py-src-op">,</span> <span class="py-src-parameter">numToads</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">numFrogs</span> <span class="py-src-op">=</span> <span class="py-src-variable">numFrogs</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">numToads</span> <span class="py-src-op">=</span> <span class="py-src-variable">numToads</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">count</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">return</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">numFrogs</span> <span class="py-src-op">+</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">numToads</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">class</span> <span class="py-src-identifier">SenderPond</span><span class="py-src-op">(</span><span class="py-src-parameter">FrogPond</span><span class="py-src-op">,</span> <span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">Copyable</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">getStateToCopy</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">d</span> <span class="py-src-op">=</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">__dict__</span><span class="py-src-op">.</span><span class="py-src-variable">copy</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span><span class="py-src-op">[</span><span class="py-src-string">'frogsAndToads'</span><span class="py-src-op">]</span> <span class="py-src-op">=</span> <span class="py-src-variable">d</span><span class="py-src-op">[</span><span class="py-src-string">'numFrogs'</span><span class="py-src-op">]</span> <span class="py-src-op">+</span> <span class="py-src-variable">d</span><span class="py-src-op">[</span><span class="py-src-string">'numToads'</span><span class="py-src-op">]</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">del</span> <span class="py-src-variable">d</span><span class="py-src-op">[</span><span class="py-src-string">'numFrogs'</span><span class="py-src-op">]</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">del</span> <span class="py-src-variable">d</span><span class="py-src-op">[</span><span class="py-src-string">'numToads'</span><span class="py-src-op">]</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">return</span> <span class="py-src-variable">d</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">class</span> <span class="py-src-identifier">ReceiverPond</span><span class="py-src-op">(</span><span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">RemoteCopy</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">setCopyableState</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">state</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">__dict__</span> <span class="py-src-op">=</span> <span class="py-src-variable">state</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">count</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">return</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">frogsAndToads</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">setUnjellyableForClass</span><span class="py-src-op">(</span><span class="py-src-variable">SenderPond</span><span class="py-src-op">,</span> <span class="py-src-variable">ReceiverPond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></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>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-op">,</span> <span class="py-src-variable">jelly</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">copy2_classes</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">SenderPond</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">Sender</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">pond</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">pond</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">got_obj</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">obj</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">d</span> <span class="py-src-op">=</span> <span class="py-src-variable">obj</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;takePond&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">ok</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">addErrback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">notOk</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">ok</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">response</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;pond arrived&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">response</span><span class="py-src-newline">
</span>        <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">notOk</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">failure</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;error during takePond:&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">if</span> <span class="py-src-variable">failure</span><span class="py-src-op">.</span><span class="py-src-variable">type</span> <span class="py-src-op">==</span> <span class="py-src-variable">jelly</span><span class="py-src-op">.</span><span class="py-src-variable">InsecureJelly</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">            </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot; InsecureJelly&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-dedent"></span><span class="py-src-keyword">else</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">            </span><span class="py-src-keyword">print</span> <span class="py-src-variable">failure</span><span class="py-src-newline">
</span>        <span class="py-src-dedent"></span><span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">return</span> <span class="py-src-variable">None</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">SenderPond</span><span class="py-src-op">(</span><span class="py-src-number">3</span><span class="py-src-op">,</span> <span class="py-src-number">4</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;count %d&quot;</span> <span class="py-src-op">%</span> <span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">count</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-variable">sender</span> <span class="py-src-op">=</span> <span class="py-src-variable">Sender</span><span class="py-src-op">(</span><span class="py-src-variable">pond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">factory</span> <span class="py-src-op">=</span> <span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">PBClientFactory</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">connectTCP</span><span class="py-src-op">(</span><span class="py-src-string">&quot;localhost&quot;</span><span class="py-src-op">,</span> <span class="py-src-number">8800</span><span class="py-src-op">,</span> <span class="py-src-variable">factory</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">deferred</span> <span class="py-src-op">=</span> <span class="py-src-variable">factory</span><span class="py-src-op">.</span><span class="py-src-variable">getRootObject</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">deferred</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">sender</span><span class="py-src-op">.</span><span class="py-src-variable">got_obj</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">run</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> <span class="py-src-op">==</span> <span class="py-src-string">'__main__'</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-variable">main</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-dedent"></span><span class="py-src-endmarker"></span></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>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-op">,</span> <span class="py-src-variable">internet</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-newline">
</span><span class="py-src-keyword">import</span> <span class="py-src-variable">copy2_classes</span> <span class="py-src-comment"># needed to get ReceiverPond registered with Jelly</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">Receiver</span><span class="py-src-op">(</span><span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">Root</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_takePond</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">pond</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot; got pond:&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">pond</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot; count %d&quot;</span> <span class="py-src-op">%</span> <span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">count</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">return</span> <span class="py-src-string">&quot;safe and sound&quot;</span> <span class="py-src-comment"># positive acknowledgement</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_shutdown</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-variable">application</span> <span class="py-src-op">=</span> <span class="py-src-variable">service</span><span class="py-src-op">.</span><span class="py-src-variable">Application</span><span class="py-src-op">(</span><span class="py-src-string">&quot;copy_receiver&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-variable">internet</span><span class="py-src-op">.</span><span class="py-src-variable">TCPServer</span><span class="py-src-op">(</span><span class="py-src-number">8800</span><span class="py-src-op">,</span> <span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">PBServerFactory</span><span class="py-src-op">(</span><span class="py-src-variable">Receiver</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">setServiceParent</span><span class="py-src-op">(</span><span class="py-src-nl">
</span>    <span class="py-src-variable">service</span><span class="py-src-op">.</span><span class="py-src-variable">IServiceCollection</span><span class="py-src-op">(</span><span class="py-src-variable">application</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></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
also sets up the binding between them. The <code>SenderPond</code> and
<code>ReceiverPond</code> are unrelated save for this binding: they happen
to implement the same methods, but use different internal instance variables
to accomplish them.</p><p>The recipient of the object doesn't even have to import the class
definition into their namespace. It is sufficient that they import the class
definition (and thus execute the <code>setUnjellyableForClass</code>
statement). The Jelly layer remembers the class definition until a matching
object is received. The sender of the object needs the definition, of
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">
% twistd -n -y copy2_receiver.py 
[-] twisted.spread.pb.PBServerFactory starting on 8800
[-] Starting factory &lt;twisted.spread.pb.PBServerFactory instance at
0x40604b4c&gt;
[Broker,0,127.0.0.1]  got pond: &lt;copy2_classes.ReceiverPond instance at
0x406eb2ac&gt;
[Broker,0,127.0.0.1]  count 7
</pre><pre class="shell">
% ./copy2_sender.py 
count 7
pond arrived safe and sound
Main loop terminated.
% 
</pre><h3>Things To Watch Out For<a name="auto7"></a></h3><ul><li>The first argument to <code>setUnjellyableForClass</code> must refer
  to the class <em>as known by the sender</em>. The sender has no way of
  knowing about how your local <code>import</code> statements are set up,
  and Python's flexible namespace semantics allow you to access the same
  class through a variety of different names. You must match whatever the
  sender does. Having both ends import the class from a separate file, using
  a canonical module name (no <q>sibiling imports</q>), is a good way to get
  this right, especially when both the sending and the receiving classes are
  defined together, with the <code>setUnjellyableForClass</code> immediately
  following them. (XXX: this works, but does this really get the right names
  into the table? Or does it only work because both are defined in the same
  (wrong) place?)</li><li>The class that is sent must inherit from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>. The class that is registered to
  receive it must inherit from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.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
  from both <code>pb.Copyable</code> and <code>pb.RemoteCopy</code>. This
  will also make it possible to send the same class symmetrically back and
  forth over the wire. But don't get confused about when it is coming (and
  using <code>setCopyableState</code>) versus when it is going (using
  <code>getStateToCopy</code>).</li><li><code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.jelly.InsecureJelly.html" title="twisted.spread.jelly.InsecureJelly">InsecureJelly</a></code>
  exceptions are raised by the receiving end. They will be delivered
  asynchronously to an <code>errback</code> handler. If you do not add one
  to the <code>Deferred</code> returned by <code>callRemote</code>, then you
  will never receive notification of the problem. </li><li>The class that is derived from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code> will be created using a
  constructor <code>__init__</code> method that takes no arguments. All
  setup must be performed in the <code>setCopyableState</code> method. As
  the docstring on <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.RemoteCopy.html" title="twisted.spread.flavors.RemoteCopy">RemoteCopy</a></code> says, don't implement a
  constructor that requires arguments in a subclass of
  <code>RemoteCopy</code>. XXX: check this, the code around
  jelly._Unjellier.unjelly:489 tries to avoid calling <code>__init__</code>
  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/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.html" title="twisted.spread.flavors">twisted.spread.flavors</a></code>, and the docstrings there are the
  best source of additional information.</li><li><code>Copyable</code> is also used in <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.web.distrib.html" title="twisted.web.distrib">twisted.web.distrib</a></code> to deliver HTTP requests to other
  programs for rendering, allowing subtrees of URL space to be delegated to
  multiple programs (on multiple machines).</li><li><code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.manhole.explorer.html" title="twisted.manhole.explorer">twisted.manhole.explorer</a></code> also uses
  <code>Copyable</code> to distribute debugging information from the program
  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
slow. <q>big</q> means it takes a lot of data (storage, network bandwidth,
processing) to represent its state. <q>slow</q> means that state doesn't
change very frequently. It may be more efficient to send the full state only
once, the first time it is needed, then afterwards only send the differences
or changes in state whenever it is modified. The <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code> class provides a framework to
implement this.</p><p><code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code> is derived
from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Copyable.html" title="twisted.spread.pb.Copyable">pb.Copyable</a></code>, so it is
based upon the idea of an object's state being captured on the sending side,
and then turned into a new object on the receiving side. This is extended to
have an object <q>publishing</q> on the sending side (derived from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Cacheable.html" title="twisted.spread.pb.Cacheable">pb.Cacheable</a></code>), matched with one
<q>observing</q> on the receiving side (derived from <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.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
to your object into accessor functions (specifically <q>setter</q>
functions). Your object needs to get control <em>every</em> single time some
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 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
add two methods: <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.Cacheable.getStateToCacheAndObserveFor.html" title="twisted.spread.flavors.Cacheable.getStateToCacheAndObserveFor">getStateToCacheAndObserveFor</a></code>
and <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.Cacheable.stoppedObserving.html" title="twisted.spread.flavors.Cacheable.stoppedObserving">stoppedObserving</a></code>. The first
is called when a remote caching reference is first created, and retrieves
the data with which the cache is first filled. It also provides an
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>

that points at that receiver-side cache. Every time the state of the object
is changed, you give a message to the observer, informing them of the
change. The other method, <code>stoppedObserving</code>, is called when the
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/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.RemoteCache.html" title="twisted.spread.pb.RemoteCache">pb.RemoteCache</a></code>, and implement the
<code>setCopyableState</code> as you would for a <code>pb.RemoteCopy</code>
object. In addition, you must implement methods to receive the updates sent
to the observer by the <code>pb.Cacheable</code>: these methods should have
names that start with <code>observe_</code>, and match the
<code>callRemote</code> invocations from the sender side just as the usual
<code>remote_*</code> and <code>perspective_*</code> methods match normal
<code>callRemote</code> calls. </p><p>The first time a reference to the <code>pb.Cacheable</code> object is
sent to any particular recipient, a sender-side Observer will be created for
it, and the <code>getStateToCacheAndObserveFor</code> method will be called
to get the current state and register the Observer. The state which that
returns is sent to the remote end and turned into a local representation
using <code>setCopyableState</code> just like <code>pb.RemoteCopy</code>,
described above (in fact it inherits from that class). </p><p>After that, your <q>setter</q> functions on the sender side should call
<code>callRemote</code> on the Observer, which causes <code>observe_*</code>
methods to run on the receiver, which are then supposed to update the
receiver-local (cached) state.</p><p>When the receiver stops following the cached object and the last
reference goes away, the <code>pb.RemoteCache</code> object can be freed.
Just before it dies, it tells the sender side it no longer cares about the
original object. When <em>that</em> reference count goes to zero, the
Observer goes away and the <code>pb.Cacheable</code> object can stop
announcing every change that takes place. The <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.Cacheable.stoppedObserving.html" title="twisted.spread.flavors.Cacheable.stoppedObserving">stoppedObserving</a></code> method is
used to tell the <code>pb.Cacheable</code> that the Observer has gone
away.</p><p>With the <code>pb.Cacheable</code> and <code>pb.RemoteCache</code>
classes in place, bound together by a call to
<code>pb.setUnjellyableForClass</code>, all that remains is to pass a
reference to your <code>pb.Cacheable</code> over the wire to the remote end.
The corresponding <code>pb.RemoteCache</code> object will automatically be
created, and the matching methods will be used to keep the receiver-side
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
controlled by the sending side, and the <code>SlaveDuckPond</code> is a
cache that tracks changes to the master:</p><div class="py-listing"><pre>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">MasterDuckPond</span><span class="py-src-op">(</span><span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">Cacheable</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">ducks</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">observers</span> <span class="py-src-op">=</span> <span class="py-src-op">[</span><span class="py-src-op">]</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">ducks</span> <span class="py-src-op">=</span> <span class="py-src-variable">ducks</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">count</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;I have [%d] ducks&quot;</span> <span class="py-src-op">%</span> <span class="py-src-variable">len</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">ducks</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">addDuck</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">duck</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">ducks</span><span class="py-src-op">.</span><span class="py-src-variable">append</span><span class="py-src-op">(</span><span class="py-src-variable">duck</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <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-op">.</span><span class="py-src-variable">observers</span><span class="py-src-op">:</span> <span class="py-src-variable">o</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">'addDuck'</span><span class="py-src-op">,</span> <span class="py-src-variable">duck</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">removeDuck</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">duck</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">ducks</span><span class="py-src-op">.</span><span class="py-src-variable">remove</span><span class="py-src-op">(</span><span class="py-src-variable">duck</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <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-op">.</span><span class="py-src-variable">observers</span><span class="py-src-op">:</span> <span class="py-src-variable">o</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">'removeDuck'</span><span class="py-src-op">,</span> <span class="py-src-variable">duck</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">getStateToCacheAndObserveFor</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">perspective</span><span class="py-src-op">,</span> <span class="py-src-parameter">observer</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">observers</span><span class="py-src-op">.</span><span class="py-src-variable">append</span><span class="py-src-op">(</span><span class="py-src-variable">observer</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-comment"># you should ignore pb.Cacheable-specific state, like self.observers
</span>        <span class="py-src-keyword">return</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">ducks</span> <span class="py-src-comment"># in this case, just a list of ducks</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">stoppedObserving</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">perspective</span><span class="py-src-op">,</span> <span class="py-src-parameter">observer</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">observers</span><span class="py-src-op">.</span><span class="py-src-variable">remove</span><span class="py-src-op">(</span><span class="py-src-variable">observer</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">class</span> <span class="py-src-identifier">SlaveDuckPond</span><span class="py-src-op">(</span><span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">RemoteCache</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span>    <span class="py-src-comment"># This is a cache of a remote MasterDuckPond
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">count</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">return</span> <span class="py-src-variable">len</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">cacheducks</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">getDucks</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">return</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">cacheducks</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">setCopyableState</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">state</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot; cache - sitting, er, setting ducks&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">cacheducks</span> <span class="py-src-op">=</span> <span class="py-src-variable">state</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">observe_addDuck</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">newDuck</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot; cache - addDuck&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">cacheducks</span><span class="py-src-op">.</span><span class="py-src-variable">append</span><span class="py-src-op">(</span><span class="py-src-variable">newDuck</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">observe_removeDuck</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">deadDuck</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot; cache - removeDuck&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">cacheducks</span><span class="py-src-op">.</span><span class="py-src-variable">remove</span><span class="py-src-op">(</span><span class="py-src-variable">deadDuck</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">setUnjellyableForClass</span><span class="py-src-op">(</span><span class="py-src-variable">MasterDuckPond</span><span class="py-src-op">,</span> <span class="py-src-variable">SlaveDuckPond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></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>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-op">,</span> <span class="py-src-variable">jelly</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">python</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">log</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">cache_classes</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">MasterDuckPond</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">Sender</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">pond</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">pond</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">phase1</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">remote</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">remote</span> <span class="py-src-op">=</span> <span class="py-src-variable">remote</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span> <span class="py-src-op">=</span> <span class="py-src-variable">remote</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;takePond&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">phase2</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">addErrback</span><span class="py-src-op">(</span><span class="py-src-variable">log</span><span class="py-src-op">.</span><span class="py-src-variable">err</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">phase2</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">response</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">addDuck</span><span class="py-src-op">(</span><span class="py-src-string">&quot;ugly duckling&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">count</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">callLater</span><span class="py-src-op">(</span><span class="py-src-number">1</span><span class="py-src-op">,</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">phase3</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">phase3</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">d</span> <span class="py-src-op">=</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">remote</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;checkDucks&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">phase4</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">addErrback</span><span class="py-src-op">(</span><span class="py-src-variable">log</span><span class="py-src-op">.</span><span class="py-src-variable">err</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">phase4</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">dummy</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">removeDuck</span><span class="py-src-op">(</span><span class="py-src-string">&quot;one duck&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">count</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">remote</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;checkDucks&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span> <span class="py-src-op">=</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">remote</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;ignorePond&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">phase5</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">phase5</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">dummy</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">d</span> <span class="py-src-op">=</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">remote</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">&quot;shutdown&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>        <span class="py-src-variable">d</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">phase6</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">phase6</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">dummy</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">main</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-variable">master</span> <span class="py-src-op">=</span> <span class="py-src-variable">MasterDuckPond</span><span class="py-src-op">(</span><span class="py-src-op">[</span><span class="py-src-string">&quot;one duck&quot;</span><span class="py-src-op">,</span> <span class="py-src-string">&quot;two duck&quot;</span><span class="py-src-op">]</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">master</span><span class="py-src-op">.</span><span class="py-src-variable">count</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span>    <span class="py-src-variable">sender</span> <span class="py-src-op">=</span> <span class="py-src-variable">Sender</span><span class="py-src-op">(</span><span class="py-src-variable">master</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">factory</span> <span class="py-src-op">=</span> <span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">PBClientFactory</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">connectTCP</span><span class="py-src-op">(</span><span class="py-src-string">&quot;localhost&quot;</span><span class="py-src-op">,</span> <span class="py-src-number">8800</span><span class="py-src-op">,</span> <span class="py-src-variable">factory</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">deferred</span> <span class="py-src-op">=</span> <span class="py-src-variable">factory</span><span class="py-src-op">.</span><span class="py-src-variable">getRootObject</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">deferred</span><span class="py-src-op">.</span><span class="py-src-variable">addCallback</span><span class="py-src-op">(</span><span class="py-src-variable">sender</span><span class="py-src-op">.</span><span class="py-src-variable">phase1</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">run</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-keyword">if</span> <span class="py-src-variable">__name__</span> <span class="py-src-op">==</span> <span class="py-src-string">'__main__'</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-variable">main</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-dedent"></span><span class="py-src-endmarker"></span></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>
<span class="py-src-comment">#! /usr/bin/python
</span><span class="py-src-nl">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-op">,</span> <span class="py-src-variable">internet</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</span><span class="py-src-variable">internet</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">reactor</span><span class="py-src-newline">
</span><span class="py-src-keyword">from</span> <span class="py-src-variable">twisted</span><span class="py-src-op">.</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-newline">
</span><span class="py-src-keyword">import</span> <span class="py-src-variable">cache_classes</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-keyword">class</span> <span class="py-src-identifier">Receiver</span><span class="py-src-op">(</span><span class="py-src-parameter">pb</span><span class="py-src-op">.</span><span class="py-src-parameter">Root</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">    </span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_takePond</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">,</span> <span class="py-src-parameter">pond</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">pond</span><span class="py-src-newline">
</span>        <span class="py-src-keyword">print</span> <span class="py-src-string">&quot;got pond:&quot;</span><span class="py-src-op">,</span> <span class="py-src-variable">pond</span> <span class="py-src-comment"># a DuckPondCache</span><span class="py-src-newline">
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">remote_checkDucks</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_checkDucks</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;[%d] ducks: &quot;</span> <span class="py-src-op">%</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">count</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-op">,</span> <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span><span class="py-src-op">.</span><span class="py-src-variable">getDucks</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_ignorePond</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span>        <span class="py-src-comment"># stop watching the pond
</span><span class="py-src-indent">        </span><span class="py-src-keyword">print</span> <span class="py-src-string">&quot;dropping pond&quot;</span><span class="py-src-newline">
</span>        <span class="py-src-comment"># gc causes __del__ causes 'decache' msg causes stoppedObserving
</span>        <span class="py-src-variable">self</span><span class="py-src-op">.</span><span class="py-src-variable">pond</span> <span class="py-src-op">=</span> <span class="py-src-variable">None</span><span class="py-src-newline">
</span>    <span class="py-src-dedent"></span><span class="py-src-keyword">def</span> <span class="py-src-identifier">remote_shutdown</span><span class="py-src-op">(</span><span class="py-src-parameter">self</span><span class="py-src-op">)</span><span class="py-src-op">:</span><span class="py-src-newline">
</span><span class="py-src-indent">        </span><span class="py-src-variable">reactor</span><span class="py-src-op">.</span><span class="py-src-variable">stop</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-nl">
</span><span class="py-src-dedent"></span><span class="py-src-dedent"></span><span class="py-src-variable">application</span> <span class="py-src-op">=</span> <span class="py-src-variable">service</span><span class="py-src-op">.</span><span class="py-src-variable">Application</span><span class="py-src-op">(</span><span class="py-src-string">&quot;copy_receiver&quot;</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-variable">internet</span><span class="py-src-op">.</span><span class="py-src-variable">TCPServer</span><span class="py-src-op">(</span><span class="py-src-number">8800</span><span class="py-src-op">,</span> <span class="py-src-variable">pb</span><span class="py-src-op">.</span><span class="py-src-variable">PBServerFactory</span><span class="py-src-op">(</span><span class="py-src-variable">Receiver</span><span class="py-src-op">(</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">setServiceParent</span><span class="py-src-op">(</span><span class="py-src-nl">
</span>    <span class="py-src-variable">service</span><span class="py-src-op">.</span><span class="py-src-variable">IServiceCollection</span><span class="py-src-op">(</span><span class="py-src-variable">application</span><span class="py-src-op">)</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></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">
% twistd -n -y cache_receiver.py 
[-] twisted.spread.pb.PBServerFactory starting on 8800
[-] Starting factory &lt;twisted.spread.pb.PBServerFactory instance at
0x40615acc&gt;
[Broker,0,127.0.0.1]  cache - sitting, er, setting ducks
[Broker,0,127.0.0.1] got pond: &lt;cache_classes.SlaveDuckPond instance at
0x406eb5ec&gt;
[Broker,0,127.0.0.1] [2] ducks:  ['one duck', 'two duck']
[Broker,0,127.0.0.1]  cache - addDuck
[Broker,0,127.0.0.1] [3] ducks:  ['one duck', 'two duck', 'ugly duckling']
[Broker,0,127.0.0.1]  cache - removeDuck
[Broker,0,127.0.0.1] [2] ducks:  ['two duck', 'ugly duckling']
[Broker,0,127.0.0.1] dropping pond
% 
</pre><pre class="shell">
% ./cache_sender.py 
I have [2] ducks
I have [3] ducks
I have [2] ducks
Main loop terminated.
% 
</pre><p>Points to notice:</p><ul><li>There is one <code>Observer</code> for each remote program that holds
  an active reference. Multiple references inside the same program don't
  matter: the serialization layer notices the duplicates and does the
  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>.
  </li><li>Multiple Observers need to be kept in a list, and all of them need to
  be updated when something changes. By sending the initial state at the
  same time as you add the observer to the list, in a single atomic action
  that cannot be interrupted by a state change, you insure that you can send
  the same status update to all the observers.</li><li>The <code>observer.callRemote</code> calls can still fail. If the
  remote side has disconnected very recently and
  <code>stoppedObserving</code> has not yet been called, you may get a
  <code>DeadReferenceError</code>. It is a good idea to add an errback to
  those <code>callRemote</code>s to throw away such an error. This is a
  useful idiom:

  <pre class="python">
<span class="py-src-variable">observer</span><span class="py-src-op">.</span><span class="py-src-variable">callRemote</span><span class="py-src-op">(</span><span class="py-src-string">'foo'</span><span class="py-src-op">,</span> <span class="py-src-variable">arg</span><span class="py-src-op">)</span><span class="py-src-op">.</span><span class="py-src-variable">addErrback</span><span class="py-src-op">(</span><span class="py-src-keyword">lambda</span> <span class="py-src-variable">f</span><span class="py-src-op">:</span> <span class="py-src-variable">None</span><span class="py-src-op">)</span><span class="py-src-newline">
</span><span class="py-src-endmarker"></span></pre>

  (XXX: verify that this is actually a concern)</li><li><code>getStateToCacheAndObserverFor</code> must return some object
  that represents the current state of the object. This may simply be the
  object's <code>__dict__</code> attribute. It is a good idea to remove the
  <code>pb.Cacheable</code>-specific members of it before sending it to the
  remote end. The list of Observers, in particular, should be left out, to
  avoid dizzying recursive Cacheable references. The mind boggles as to the
  potential consequences of leaving in such an item.</li><li>A <code>perspective</code> argument is available to
  <code>getStateToCacheAndObserveFor</code>, as well as
  <code>stoppedObserving</code>. I think the purpose of this is to allow
  viewer-specific changes to the way the cache is updated. If all remote
  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
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/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.html" title="twisted.spread.flavors">twisted.spread.flavors</a></code>, where <code>pb.Cacheable</code>
  is implemented.</li><li><code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.manhole.explorer.html" title="twisted.manhole.explorer">twisted.manhole.explorer</a></code> uses
  <code>Cacheable</code>, and does some fairly interesting things with it.
  (XXX: I've heard explorer is currently broken, it might not be a good
  example to recommend)</li><li>The <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.publish.html" title="twisted.spread.publish">spread.publish</a></code> module also
  uses <code>Cacheable</code>, and might be a source of further
  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
a verb with the opposite meaning of <q>jelly</q>. The verb <q>to jelly</q>
means to serialize an object or data structure into a sequence of bytes (or
other primitive transmittable/storable representation), while <q>to
unjelly</q> means to unserialize the bytestream into a live object in the
receiver's memory space. <q>Unjellyable</q> is a noun, (<em>not</em> an
adjective), referring to the the class that serves as a destination or
recipient of the unjellying process. <q>A is unjellyable into B</q> means
that a serialized representation A (of some remote object) can be
unserialized into a local object of type B. It is these objects <q>B</q>
that are the <q>Unjellyable</q> second argument of the
<code>setUnjellyableForClass</code> function.</p><p>In particular, <q>unjellyable</q> does <em>not</em> mean <q>cannot be
jellied</q>. <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.jelly.Unpersistable.html" title="twisted.spread.jelly.Unpersistable">Unpersistable</a></code> means <q>not
persistable</q>, but <q>unjelly</q>, <q>unserialize</q>, and <q>unpickle</q>
mean to reverse the operations of <q>jellying</q>, <q>serializing</q>, and
<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/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.RemoteCopy.html" title="twisted.spread.pb.RemoteCopy">pb.RemoteCopy</a></code> is actually defined
  as <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.RemoteCopy.html" title="twisted.spread.flavors.RemoteCopy">flavors.RemoteCopy</a></code>, but
  <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
add a hook to <code>__setattr__</code>, along with magical change-announcing
subclasses of the usual builtin types, to detect changes that result from
normal <q>=</q> set operations. The result might be 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/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.flavors.RemoteCacheObserver.html" title="twisted.spread.flavors.RemoteCacheObserver">RemoteCacheObserver</a></code>, but it isn't very
useful to subclass or modify, so simply treat it as a little demon that sits
in your <code>pb.Cacheable</code> class and helps you distribute change
notifications. The only useful thing to do with it is to run its
<code>callRemote</code> method, which acts just like a normal
<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
  multiple references through the same <code class="API"><a href="http://twistedmatrix.com/documents/TwistedDocs/TwistedDocs-1.3.0/api/twisted.spread.pb.Broker.html" title="twisted.spread.pb.Broker">Broker</a></code>. If you've managed to make multiple
  TCP connections to the same program, you deserve whatever you get.</span></a></li></ol></div><p><a href="../howto/index.html">Index</a></p><span class="version">Version: 1.3.0</span></body></html>