+2007-05-14 Brian Warner <warner@lothar.com>
+
+ * foolscap/__init__.py: release Foolscap-0.1.4
+ * misc/{sid|sarge|dapper|edgy|feisty}/debian/changelog: same, also
+ remove a bunch of old between-release version numbers
+
+2007-05-14 Brian Warner <warner@lothar.com>
+
+ * NEWS: update for the upcoming release
+
+ * doc/using-foolscap.xhtml: rename from doc/using-pb.xhtml
+
+ * doc/using-pb.xhtml: replace all uses of 'PB URL' with 'FURL'
+
+ * foolscap/pb.py (Tub.getReference): if getReference() is called
+ before Tub.startService(), queue the request until startup.
+ (Tub.connectTo): same for connectTo().
+ (Tub.startService): launch pending getReference() and connectTo()
+ requests. There are all fired with eventual-sends.
+ * foolscap/reconnector.py (Reconnector): don't automatically start
+ the Reconnector in __init__, rather wait for the Tub to start it.
+ * foolscap/test/test_tub.py (QueuedStartup): test it
+ * doc/using-pb.xhtml: update docs to match
+
+ * foolscap/test/test_call.py (TestCall.testCall1): replace an
+ arbitrary delay with a polling loop, to make the test more
+ reliable under load
+
+ * foolscap/referenceable.py (SturdyRef.asLiveRef): remove a method
+ that was never used, didn't work, and is of dubious utility
+ anyways.
+ (_AsLiveRef): remove this too
+
+ * misc/testutils/figleaf.py (CodeTracer.start): remove leftover
+ debug logging
+
+ * foolscap/remoteinterface.py (RemoteInterfaceConstraint): accept
+ gifts too: allow sending of RemoteReferences on the outbound side,
+ and accept their-reference sequences on the inbound side.
+ * foolscap/test/test_gifts.py (Gifts.test_constraint): test it
+ * foolscap/test/test_schema.py (Interfaces.test_remotereference):
+ update test, since now we allow RemoteReferences to be sent on the
+ outbound side
+
+ * foolscap/remoteinterface.py (getRemoteInterface): improve the
+ error message reported when a Referenceable class implements
+ multiple RemoteInterfaces
+
+ * foolscap/remoteinterface.py (RemoteMethodSchema.initFromMethod):
+ properly handle methods like 'def foo(nodefault)' that are missing
+ *all* default values. Previously this resulted in an unhelpful
+ exception (since typeList==None), now it gives a sensible
+ InvalidRemoteInterface exception.
+ * foolscap/test/test_schema.py (Arguments.test_bad_arguments):
+ test it
+
+2007-05-11 Brian Warner <warner@lothar.com>
+
+ * foolscap/slicers/set.py (FrozenSetSlicer): finally acknowledge
+ our dependence on python2.4 or newer, by using the built-in 'set'
+ and 'frozenset' types by default. We'll serialize the old sets.Set
+ and sets.ImmutableSet too, but they'll emerge as a set/frozenset.
+ This will cause problems for code that was written to be
+ compatible with python2.3 (by using sets.Set) and wasn't changed
+ when moved to 2.4, if it tries to mingle sets.Set with the data
+ coming out of Foolscap. Unfortunate, but doing it this way
+ preserves both sanity and good behavior for modern 2.4-or-later
+ apps.
+ (SetUnslicer): fix handling of children that were unreferenceable
+ during construction, fix handling of children that are not ready
+ for use (i.e. gifts).
+ (FrozenSetUnslicer): base this off of TupleUnslicer, since
+ previously the cycle-handling logic was completely broken. I'm not
+ entirely sure this is necessary, since I think the contents of
+ sets must be transitively immutable (or at least transitively
+ hashable), but it good to review and clean it up anyways.
+ * foolscap/slicers/allslicers.py: match name change
+
+ * foolscap/slicers/tuple.py (TupleUnslicer.receiveClose): fix
+ handling of unready children (i.e. gifts), previously gifts inside
+ containers were completely broken.
+ * foolscap/slicers/list.py (ListUnslicer.receiveClose): same
+ * foolscap/slicers/dict.py (DictUnslicer.receiveClose): same
+
+ * foolscap/call.py: add debug log messages (disabled)
+
+ * foolscap/referenceable.py (TheirReferenceUnslicer.receiveClose):
+ gifts must declare themselves 'unready' until the RemoteReference
+ resolves, since we might be inside a container of some sort.
+ Without this fix, methods would be invoked too early, before the
+ RemoteReference was really available.
+
+ * foolscap/test/test_banana.py (ThereAndBackAgain.test_set): match
+ new set/sets.Set behavior
+ (ThereAndBackAgain.test_cycles_1): test some of the cycles
+ (ThereAndBackAgain.test_cycles_3): add (disabled) test for
+ checking cycles that involve sets. I think these tests are
+ non-sensical, since sets can't really participate in the sorts of
+ cycles we worry about, but I left the (disabled) test code in
+ place in case it becomes useful again.
+
+ * foolscap/test/test_gifts.py (Gifts.testContainers): validate
+ that gifts can appear in all sorts of containers successfully.
+
+2007-05-11 Brian Warner <warner@lothar.com.com>
+
+ * foolscap/__init__.py: bump revision to 0.1.3+ while between releases
+ * misc/{sid|sarge|dapper|edgy|feisty}/debian/changelog: same
+
2007-05-02 Brian Warner <warner@lothar.com>
* foolscap/__init__.py: release Foolscap-0.1.3
User visible changes in Foolscap (aka newpb/pb2). -*- outline -*-
+* Release 0.1.4 (14 May 2007)
+
+** Compatibility
+
+This release is fully compatible with 0.1.3 .
+
+** getReference/connectTo can be called before Tub.startService()
+
+The Tub.startService changes that were suggested in the 0.1.3 release notes
+have been implemented. Calling getReference() or connectTo() before the Tub
+has been started is now allowed, however no action will take place until the
+Tub is running. Don't forget to start the Tub, or you'll be left wondering
+why your Deferred or callback is never fired. (A log message is emitted when
+these calls are made before the Tub is started, in the hopes of helping
+developers find this mistake faster).
+
+** constraint improvements
+
+The RIFoo -style constraint now accepts gifts (third-party references). This
+also means that using RIFoo on the outbound side will accept either a
+Referenceable that implements the given RemoteInterface or a RemoteReference
+that points to a Referenceable that implements the given RemoteInterface.
+There is a situation (sending a RemoteReference back to its owner) that will
+pass the outbound constraint but be rejected by the inbound constraint on the
+other end. It remains to be seen how this will be fixed.
+
+** foolscap now deserializes into python2.4-native 'set' and 'frozenset' types
+
+Since Foolscap is dependent upon python2.4 or newer anyways, it now
+unconditionally creates built-in 'set' and 'frozenset' instances when
+deserializing 'set'/'immutable-set' banana sequences. The pre-python2.4
+'sets' module has non-built-in set classes named sets.Set and
+sets.ImmutableSet, and these are serialized just like the built-in forms.
+
+Unfortunately this means that Set and ImmutableSet will not survive a
+round-trip: they'll be turned into set and frozenset, respectively. Worse
+yet, 'set' and 'sets.Set' are not entirely compatible. This may cause a
+problem for older applications that were written to be compatible with both
+python-2.3 and python-2.4 (by using sets.Set/sets.ImmutableSet), for which
+the compatibility code is still in place (i.e. they are not using
+set/frozenset). These applications may experience problems when set objects
+that traverse the wire via Foolscap are brought into close proximity with set
+objects that remained local. This is unfortunate, but it's the cleanest way
+to support modern applications that use the native types exclusively.
+
+** bug fixes
+
+Gifts inside containers (lists, tuples, dicts, sets) were broken: the target
+method was frequently invoked before the gift had properly resolved into a
+RemoteReference. Constraints involving gifts inside containers were broken
+too. The constraints may be too loose right now, but I don't think they
+should cause false negatives.
+
+The unused SturdyRef.asLiveRef method was removed, since it didn't work
+anyways.
+
+** terminology shift: FURL
+
+The preferred name for the sort of URL that you get back from
+registerReference (and hand to getReference or connectTo) has changed from
+"PB URL" to "FURL" (short for Foolscap URL). They still start with 'pb:',
+however. Documentation is slowly being changed to use this term.
+
+
* Release 0.1.3 (02 May 2007)
** Incompatibility Warning
--- /dev/null
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>Introduction to Foolscap</title>
+<style src="stylesheet-unprocessed.css"></style>
+</head>
+
+<body>
+<h1>Introduction to Foolscap</h1>
+
+<h2>Introduction</h2>
+
+<p>Suppose you find yourself in control of both ends of the wire: you have
+two programs that need to talk to each other, and you get to use any protocol
+you want. If you can think of your problem in terms of objects that need to
+make method calls on each other, then chances are good that you can use the
+Foolscap protocol rather than trying to shoehorn your needs into something
+like HTTP, or implementing yet another RPC mechanism.</p>
+
+<p>Foolscap is based upon a few central concepts:</p>
+
+<ul>
+
+ <li><em>serialization</em>: taking fairly arbitrary objects and types,
+ turning them into a chunk of bytes, sending them over a wire, then
+ reconstituting them on the other end. By keeping careful track of object
+ ids, the serialized objects can contain references to other objects and the
+ remote copy will still be useful. </li>
+
+ <li><em>remote method calls</em>: doing something to a local proxy and
+ causing a method to get run on a distant object. The local proxy is called
+ a <code class="API" base="foolscap.referenceable">RemoteReference</code>,
+ and you <q>do something</q> by running its <code>.callRemote</code> method.
+ The distant object is called a <code class="API"
+ base="foolscap.referenceable">Referenceable</code>, and it has methods like
+ <code>remote_foo</code> that will be invoked.</li>
+
+</ul>
+
+<p>Foolscap is the descendant of Perspective Broker (which lived in the
+twisted.spread package). For many years it was known as "newpb". A lot of the
+API still has the name "PB" in it somewhere. These will probably go away
+sooner or later.</p>
+
+<p>A "foolscap" is a size of paper, probably measuring 17 by 13.5 inches. A
+twisted foolscap of paper makes a good fool's cap. Also, "cap" makes me think
+of capabilities, and Foolscap is a protocol to implement a distributed
+object-capabilities model in python.</p>
+
+
+<h2>Getting Started</h2>
+
+<p>Any Foolscap application has at least two sides: one which hosts a
+remotely-callable object, and another which calls (remotely) the methods of
+that object. We'll start with a simple example that demostrates both ends.
+Later, we'll add more features like RemoteInterface declarations, and
+transferring object references.</p>
+
+<p>The most common way to make an object with remotely-callable methods is to
+subclass <code class="API"
+base="foolscap.referenceable">Referenceable</code>. Let's create a simple
+server which does basic arithmetic. You might use such a service to perform
+difficult mathematical operations, like addition, on a remote machine which
+is faster and more capable than your own<span class="footnote">although
+really, if your client machine is too slow to perform this kind of math, it
+is probably too slow to run python or use a network, so you should seriously
+consider a hardware upgrade</span>.</p>
+
+<pre class="python">
+from foolscap import Referenceable
+
+class MathServer(Referenceable):
+ def remote_add(self, a, b):
+ return a+b
+ def remote_subtract(self, a, b):
+ return a-b
+ def remote_sum(self, args):
+ total = 0
+ for a in args: total += a
+ return total
+
+myserver = MathServer()
+</pre>
+
+<p>On the other end of the wire (which you might call the <q>client</q>
+side), the code will have a <code class="API"
+base="foolscap.referenceable">RemoteReference</code> to this object. The
+<code>RemoteReference</code> has a method named <code class="API"
+base="foolscap.referenceable.RemoteReference">callRemote</code> which you
+will use to invoke the method. It always returns a Deferred, which will fire
+with the result of the method. Assuming you've already acquired the
+<code>RemoteReference</code>, you would invoke the method like this:</p>
+
+<pre class="python">
+def gotAnswer(result):
+ print "result is", result
+def gotError(err):
+ print "error:", err
+d = remote.callRemote("add", 1, 2)
+d.addCallbacks(gotAnswer, gotError)
+</pre>
+
+<p>Ok, now how do you acquire that <code>RemoteReference</code>? How do you
+make the <code>Referenceable</code> available to the outside world? For this,
+we'll need to discuss the <q>Tub</q>, and the concept of a <q>FURL
+URL</q>.</p>
+
+<h2>Tubs: The Foolscap Service</h2>
+
+<p>The <code class="API" base="foolscap.pb">Tub</code> is the container that
+you use to publish <code>Referenceable</code>s, and is the middle-man you use
+to access <code>Referenceable</code>s on other systems. It is known as the
+<q>Tub</q>, since it provides similar naming and identification properties as
+the <a href="http://www.erights.org/">E language</a>'s <q>Vat</q><span
+class="footnote">but they do not provide quite the same insulation against
+other objects as E's Vats do. In this sense, Tubs are leaky Vats.</span>. If
+you want to make a <code>Referenceable</code> available to the world, you
+create a Tub, tell it to listen on a TCP port, and then register the
+<code>Referenceable</code> with it under a name of your choosing. If you want
+to access a remote <code>Referenceable</code>, you create a Tub and ask it to
+acquire a <code>RemoteReference</code> using that same name.</p>
+
+<p>The <code>Tub</code> is a Twisted <code class="API"
+base="twisted.application.service">Service</code> subclass, so you use it in
+the same way: once you've created one, you attach it to a parent Service or
+Application object. Once the top-level Application object has been started,
+the Tub will start listening on any network ports you've requested. When the
+Tub is shut down, it will stop listening and drop any connections it had
+established since last startup. If you have no parent to attach it to, you
+can use <code>startService</code> and <code>stopService</code> on the Tub
+directly.</p>
+
+<p>Note that no network activity will occur until the Tub's
+<code>startService</code> method has been called. This means that any
+<code>getReference</code> or <code>connectTo</code> requests that occur
+before the Tub is started will be deferred until startup. If the program
+forgets to start the Tub, these requests will never be serviced. A message to
+this effect is added to the twistd.log file to help developers discover this
+kind of problem.</p>
+
+<h3>Making your Tub remotely accessible</h3>
+
+<p>To make any of your <code>Referenceable</code>s available, you must make
+your Tub available. There are three parts: give it an identity, have it
+listen on a port, and tell it the protocol/hostname/portnumber at which that
+port is accessibly to the outside world.</p>
+
+<p>In general, the Tub will generate its own identity, the <em>TubID</em>, by
+creating an SSL private key certificate and hashing it into a suitably-long
+random-looking string. This is the primary identifier of the Tub: everything
+else is just a <em>location hint</em> that suggests how the Tub might be
+reached. The fact that the TubID is tied to the private key allows FURLs to
+be <q>secure</q> references (meaning that no third party can cause you to
+connect to the wrong reference). You can also create a Tub with a
+pre-existing certificate, which is how Tubs can retain a persistent identity
+over multiple executions.</p>
+
+<p>You can also create an <code>UnauthenticatedTub</code>, which has an empty
+TubID. Hosting and connecting to unauthenticated Tubs do not require the
+pyOpenSSL library, but do not provide privacy, authentication, connection
+redirection, or shared listening ports. The FURLs that point to
+unauthenticated Tubs have a distinct form (starting with <code>pbu:</code>
+instead of <code>pb:</code>) to make sure they are not mistaken for
+authenticated Tubs. Foolscap uses authenticated Tubs by default.</p>
+
+<p>Having the Tub listen on a TCP port is as simple as calling <code
+class="API" base="foolscap.pb.Tub">listenOn</code> with a <code class="API"
+base="twisted.application">strports</code>-formatted port specification
+string. The simplest such string would be <q>tcp:12345</q>, to listen on port
+12345 on all interfaces. Using <q>tcp:12345:interface=127.0.0.1</q> would
+cause it to only listen on the localhost interface, making it available only
+to other processes on the same host. The <code>strports</code> module
+provides many other possibilities.</p>
+
+<p>The Tub needs to be told how it can be reached, so it knows what host and
+port to put into the URLs it creates. This location is simply a string in the
+format <q>host:port</q>, using the host name by which that TCP port you've
+just opened can be reached. Foolscap cannot, in general, guess what this name
+is, especially if there are NAT boxes or port-forwarding devices in the way.
+If your machine is reachable directly over the internet as
+<q>myhost.example.com</q>, then you could use something like this:</p>
+
+<pre class="python">
+from foolscap import Tub
+
+tub = Tub()
+tub.listenOn("tcp:12345") # start listening on TCP port 12345
+tub.setLocation("myhost.example.com:12345")
+</pre>
+
+<h3>Registering the Referenceable</h3>
+
+<p>Once the Tub has a Listener and a location, you can publish your
+<code>Referenceable</code> to the entire world by picking a name and
+registering it:</p>
+
+<pre class="python">
+url = tub.registerReference(myserver, "math-service")
+</pre>
+
+<p>This returns the <q>FURL</q> for your <code>Referenceable</code>. Remote
+systems will use this URL to access your newly-published object. The
+registration just maps a per-Tub name to the <code>Referenceable</code>:
+technically the same <code>Referenceable</code> could be published multiple
+times, under different names, or even be published by multiple Tubs in the
+same application. But in general, each program will have exactly one Tub, and
+each object will be registered under only one name.</p>
+
+<p>In this example (if we pretend the generated TubID was <q>ABCD</q>), the
+URL returned by <code>registerReference</code> would be
+<code>"pb://ABCD@myhost.example.com:12345/math-service"</code>.</p>
+
+<p>If you do not provide a name, a random (and unguessable) name will be
+generated for you. This is useful when you want to give access to your
+<code>Referenceable</code> to someone specific, but do not want to make it
+possible for someone else to acquire it by guessing the name.</p>
+
+<p>To use an unauthenticated Tub instead, you would do the following:</p>
+<pre class="python">
+from foolscap import UnauthenticatedTub
+
+tub = UnauthenticatedTub()
+tub.listenOn("tcp:12345") # start listening on TCP port 12345
+tub.setLocation("myhost.example.com:12345")
+url = tub.registerReference(myserver, "math-service")
+</pre>
+
+<p>In this case, the URL would be
+<code>"pbu://myhost.example.com:12345/math-service"</code>. The deterministic
+nature of this form makes it slightly easier to throw together
+quick-and-dirty Foolscap applications, since you only need to hard-code the
+target host and port into the client side program. However any serious
+application should just used the default authenticated form and use a full
+URL as their starting point. Note that the URL can come from anywhere: typed
+in by the user, retrieved from a web page, or hardcoded into the
+application.</p>
+
+<h4>Using a persistent certificate</h4>
+
+<p>The Tub uses a TLS private-key certificate as the base of all its
+cryptographic operations. If you don't give it one when you create the Tub,
+it will generate a brand-new one.</p>
+
+<p>The TubID is simply the hash of this certificate, so if you are writing an
+application that should have a stable long-term identity, you will need to
+insure that the Tub uses the same certificate every time your app starts. The
+easiest way to do this is to pass the <code>certFile=</code> argument into
+your <code>Tub()</code> constructor call. This argument provides a filename
+where you want the Tub to store its certificate. The first time the Tub is
+started (when this file does not exist), the Tub will generate a new
+certificate and store it here. On subsequent invocations, the Tub will read
+the earlier certificate from this location. Make sure this filename points to
+a writable location, and that you pass the same filename to
+<code>Tub()</code> each time.</p>
+
+<h3>Retrieving a RemoteReference</h3>
+
+<p>On the <q>client</q> side, you also need to create a Tub, although you
+don't need to perform the (<code>listenOn</code>, <code>setLocation</code>,
+<code>registerReference</code>) sequence unless you are also publishing
+<code>Referenceable</code>s to the world. To acquire a reference to somebody
+else's object, just use <code class="API"
+base="foolscap.pb.Tub">getReference</code>:</p>
+
+<pre class="python">
+from foolscap import Tub
+
+tub = Tub()
+tub.startService()
+d = tub.getReference("pb://ABCD@myhost.example.com:12345/math-service")
+def gotReference(remote):
+ print "Got the RemoteReference:", remote
+def gotError(err):
+ print "error:", err
+d.addCallbacks(gotReference, gotError)
+</pre>
+
+<p><code>getReference</code> returns a Deferred which will fire with a
+<code>RemoteReference</code> that is connected to the remote
+<code>Referenceable</code> named by the URL. It will use an existing
+connection, if one is available, and it will return an existing
+<code>RemoteReference</code>, it one has already been acquired.</p>
+
+<p>Since <code>getReference</code> requests are queued until the Tub starts,
+the following will work too. But don't forget to call
+<code>tub.startService()</code> eventually, otherwise your program will hang
+forever.</p>
+
+<pre class="python">
+from foolscap import Tub
+
+tub = Tub()
+d = tub.getReference("pb://ABCD@myhost.example.com:12345/math-service")
+def gotReference(remote):
+ print "Got the RemoteReference:", remote
+def gotError(err):
+ print "error:", err
+d.addCallbacks(gotReference, gotError)
+tub.startService()
+</pre>
+
+
+<h3>Complete example</h3>
+
+<p>Here are two programs, one implementing the server side of our
+remote-addition protocol, the other behaving as a client. This first example
+uses an unauthenticated Tub so you don't have to manually copy a URL from the
+server to the client. Both of these are standalone programs (you just run
+them), but normally you would create an <code class="API"
+base="twisted.application.service">Application</code> object and pass the
+file to <code>twistd -noy</code>. An example of that usage will be provided
+later.</p>
+
+<a href="listings/pb1server.py" class="py-listing"
+skipLines="2">pb1server.py</a>
+
+<a href="listings/pb1client.py" class="py-listing"
+skipLines="2">pb1client.py</a>
+
+<pre class="shell">
+% doc/listings/pb1server.py
+the object is available at: pbu://localhost:12345/math-service
+</pre>
+
+<pre class="shell">
+% doc/listings/pb1client.py
+got a RemoteReference
+asking it to add 1+2
+the answer is 3
+%
+</pre>
+
+<p>The second example uses authenticated Tubs. When running this example, you
+must copy the URL printed by the server and provide it as an argument to the
+client.</p>
+
+<a href="listings/pb2server.py" class="py-listing"
+skipLines="2">pb2server.py</a>
+
+<a href="listings/pb2client.py" class="py-listing"
+skipLines="2">pb2client.py</a>
+
+<pre class="shell">
+% doc/listings/pb2server.py
+the object is available at: pb://abcd123@localhost:12345/math-service
+</pre>
+
+<pre class="shell">
+% doc/listings/pb2client.py pb://abcd123@localhost:12345/math-service
+got a RemoteReference
+asking it to add 1+2
+the answer is 3
+%
+</pre>
+
+
+<h3>FURLs</h3>
+
+<p>In Foolscap, each world-accessible Referenceable has one or more URLs
+which are <q>secure</q>, where we use the capability-security definition of
+the term, meaning those URLs have the following properties:</p>
+
+<ul>
+ <li>The only way to acquire the URL is either to get it from someone else
+ who already has it, or to be the person who published it in the first
+ place.</li>
+
+ <li>Only that original creator of the URL gets to determine which
+ Referenceable it will connect to. If your
+ <code>tub.getReference(url)</code> call succeeds, the Referenceable you
+ will be connected to will be the right one.</li>
+</ul>
+
+<p>To accomplish the first goal, FURLs must be unguessable. You can register
+the reference with a human-readable name if your intention is to make it
+available to the world, but in general you will let
+<code>tub.registerReference</code> generate a random name for you, preserving
+the unguessability property.</p>
+
+<p>To accomplish the second goal, the cryptographically-secure TubID is used
+as the primary identifier, and the <q>location hints</q> are just that:
+hints. If DNS has been subverted to point the hostname at a different
+machine, or if a man-in-the-middle attack causes you to connect to the wrong
+box, the TubID will not match the remote end, and the connection will be
+dropped. These attacks can cause a denial-of-service, but they cannot cause
+you to mistakenly connect to the wrong target.</p>
+
+<p>Obviously this second property only holds if you use SSL. If you choose to
+use unauthenticated Tubs, all security properties are lost.</p>
+
+<p>The format of a FURL, like
+<code>pb://abcd123@example.com:5901,backup.example.com:8800/math-server</code>,
+is as follows<span class="footnote">note that the FURL uses the same format
+as an <a href="http://www.waterken.com/dev/YURL/httpsy/">HTTPSY</a>
+URL</span>:</p>
+
+<ol>
+ <li>The literal string <code>pb://</code></li>
+ <li>The TubID (as a base32-encoded hash of the SSL certificate)</li>
+ <li>A literal <code>@</code> sign</li>
+
+ <li>A comma-separated list of <q>location hints</q>. Each is one of the
+ following:
+ <ul>
+ <li>TCP over IPv4 via DNS: <code>HOSTNAME:PORTNUM</code></li>
+ <li>TCP over IPv4 without DNS: <code>A.B.C.D:PORTNUM</code></li>
+ <li>TCP over IPv6: (TODO, maybe <code>tcp6:HOSTNAME:PORTNUM</code> ?</li>
+ <li>TCP over IPv6 w/o DNS: (TODO,
+ maybe <code>tcp6:[X:Y::Z]:PORTNUM</code></li>
+ <li>Unix-domain socket: (TODO)</li>
+ </ul>
+
+ Each location hint is attempted in turn. Servers can return a
+ <q>redirect</q>, which will cause the client to insert the provided
+ redirect targets into the hint list and start trying them before continuing
+ with the original list.</li>
+
+ <li>A literal <code>/</code> character</li>
+ <li>The reference's name</li>
+</ol>
+
+<p>(Unix-domain sockets are represented with only a single location hint, in
+the format <code>pb://ABCD@unix/path/to/socket/NAME</code>, but this needs
+some work)</p>
+
+<p>FURLs for unauthenticated Tubs, like
+<code>pbu://example.com:8700/math-server</code>, are formatted as
+follows:</p>
+
+<ol>
+ <li>The literal string <code>pbu://</code></li>
+ <li>A single location hint</li>
+ <li>A literal <code>/</code> character</li>
+ <li>The reference's name</li>
+</ol>
+
+<h2>Clients vs Servers, Names and Capabilities</h2>
+
+<p>It is worthwhile to point out that Foolscap is a symmetric protocol.
+<code>Referenceable</code> instances can live on either side of a wire, and
+the only difference between <q>client</q> and <q>server</q> is who publishes
+the object and who initiates the network connection.</p>
+
+<p>In any Foolscap-using system, the very first object exchanged must be
+acquired with a <code>tub.getReference(url)</code> call<span
+class="footnote">in fact, the very <em>very</em> first object exchanged is a
+special implicit RemoteReference to the remote Tub itself, which implements
+an internal protocol that includes a method named
+<code>remote_getReference</code>. The <code>tub.getReference(url)</code> call
+is turned into one step that connects to the remote Tub, and a second step
+which invokes remotetub.callRemote("getReference", refname) on the
+result</span>, which means it must have been published with a call to
+<code>tub.registerReference(ref, name)</code>. After that, other objects can
+be passed as an argument to (or a return value from) a remotely-invoked
+method of that first object. Any suitable <code>Referenceable</code> object
+that is passed over the wire will appear on the other side as a corresponding
+<code>RemoteReference</code>. It is not necessary to
+<code>registerReference</code> something to let it pass over the wire.</p>
+
+<p>The converse of this property is thus: if you do <em>not</em>
+<code>registerReference</code> a particular <code>Referenceable</code>, and
+you do <em>not</em> give it to anyone else (by passing it in an argument to
+somebody's remote method, or return it from one of your own), then nobody
+else will be able to get access to that <code>Referenceable</code>. This
+property means the <code>Referenceable</code> is a <q>capability</q>, as
+holding a corresponding <code>RemoteReference</code> gives someone a power
+that they cannot acquire in any other way<span class="footnote">of course,
+the Foolscap connections must be secured with SSL (otherwise an eavesdropper
+or man-in-the-middle could get access), and the registered name must be
+unguessable (or someone else could acquire a reference), but both of these
+are the default.</span></p>
+
+<p>In the following example, the first program creates an RPN-style
+<code>Calculator</code> object which responds to <q>push</q>, <q>pop</q>,
+<q>add</q>, and <q>subtract</q> messages from the user. The user can also
+register an <code>Observer</code>, to which the Calculator sends an
+<code>event</code> message each time something happens to the calculator's
+state. When you consider the <code>Calculator</code> object, the first
+program is the server and the second program is the client. When you think
+about the <code>Observer</code> object, the first program is a client and the
+second program is the server. It also happens that the first program is
+listening on a socket, while the second program initiated a network
+connection to the first. It <em>also</em> happens that the first program
+published an object under some well-known name, while the second program has
+not published any objects. These are all independent properties.</p>
+
+<p>Also note that the Calculator side of the example is implemented using a
+<code class="API" base="twisted.application.service">Application</code>
+object, which is the way you'd normally build a real-world application. You
+therefore use <code>twistd</code> to launch the program. The User side is
+written with the same <code>reactor.run()</code> style as the earlier
+example.</p>
+
+<p>The server registers the Calculator instance and prints the FURL at which
+it is listening. You need to pass this FURL to the client program so it knows
+how to contact the servre. If you have a modern version of Twisted (2.5 or
+later) and the right encryption libraries installed, you'll get an
+authenticated Tub (for which the FURL will start with "pb:" and will be
+fairly long). If you don't, you'll get an unauthenticated Tub (with a
+relatively short FURL that starts with "pbu:").</p>
+
+<a href="listings/pb3calculator.py" class="py-listing"
+skipLines="2">pb3calculator.py</a>
+
+<a href="listings/pb3user.py" class="py-listing"
+skipLines="2">pb3user.py</a>
+
+<pre class="shell">
+% twistd -noy doc/listings/pb3calculator.py
+15:46 PDT [-] Log opened.
+15:46 PDT [-] twistd 2.4.0 (/usr/bin/python 2.4.4) starting up
+15:46 PDT [-] reactor class: twisted.internet.selectreactor.SelectReactor
+15:46 PDT [-] Loading doc/listings/pb3calculator.py...
+15:46 PDT [-] the object is available at:
+ pb://5ojw4cv4u4d5cenxxekjukrogzytnhop@localhost:12345/calculator
+15:46 PDT [-] Loaded.
+15:46 PDT [-] foolscap.pb.Listener starting on 12345
+15:46 PDT [-] Starting factory <Listener at 0x4869c0f4 on tcp:12345
+ with tubs None>
+</pre>
+
+<pre class="shell">
+% doc/listings/pb3user.py \
+ pb://5ojw4cv4u4d5cenxxekjukrogzytnhop@localhost:12345/calculator
+event: push(2)
+event: push(3)
+event: add
+event: pop
+the result is 5
+%
+</pre>
+
+
+<h2>Invoking Methods, Method Arguments</h2>
+
+<p>As you've probably already guessed, all the methods with names that begin
+with <code>remote_</code> will be available to anyone who manages to acquire
+a corresponding <code>RemoteReference</code>. <code>remote_foo</code> matches
+a <code>ref.callRemote("foo")</code>, etc. This name lookup can be changed by
+overriding <code>Referenceable</code> (or, perhaps more usefully,
+implementing an <code class="API"
+base="foolscap.ipb">IRemotelyCallable</code> adapter).</p>
+
+<p>The arguments of a remote method may be passed as either positional
+parameters (<code>foo(1,2)</code>), or as keyword args
+(<code>foo(a=1,b=2)</code>), or a mixture of both. The usual python rules
+about not duplicating parameters apply.</p>
+
+<p>You can pass all sorts of normal objects to a remote method: strings,
+numbers, tuples, lists, and dictionaries. The serialization of these objects
+is handled by <a href="specifications/banana.xhtml">Banana</a>, which knows
+how to convey arbitrary object graphs over the wire. Things like containers
+which contain multiple references to the same object, and recursive
+references (cycles in the object graph) are all handled correctly<span
+class="footnote">you may not want to accept shared objects in your method
+arguments, as it could lead to surprising behavior depending upon how you
+have written your method. The <code class="API"
+base="foolscap.schema">Shared</code> constraint will let you express this,
+and is described in the <a href="#constraints">Constraints</a> section of
+this document</span>.</p>
+
+<p>Passing instances is handled specially. Foolscap will not send anything
+over the wire that it does not know how to serialize, and (unlike the
+standard <code>pickle</code> module) it will not make assumptions about how
+to handle classes that that have not been explicitly marked as serializable.
+This is for security, both for the sender (making you you don't pass anything
+over the wire that you didn't intend to let out of your security perimeter),
+and for the recipient (making sure outsiders aren't allowed to create
+arbitrary instances inside your memory space, and therefore letting them run
+somewhat arbitrary code inside <em>your</em> perimeter).</p>
+
+<p>Sending <code>Referenceable</code>s is straightforward: they always appear
+as a corresponding <code>RemoteReference</code> on the other side. You can
+send the same <code>Referenceable</code> as many times as you like, and it
+will always show up as the same <code>RemoteReference</code> instance. A
+distributed reference count is maintained, so as long as the remote side
+hasn't forgotten about the <code>RemoteReference</code>, the original
+<code>Referenceable</code> will be kept alive.</p>
+
+<p>Sending <code>RemoteReference</code>s fall into two categories. If you are
+sending a <code>RemoteReference</code> back to the Tub that you got it from,
+they will see their original <code>Referenceable</code>. If you send it to
+some other Tub, they will (eventually) see a <code>RemoteReference</code> of
+their own. This last feature is called an <q>introduction</q>, and has a few
+additional requirements: see the <a href="#introductions">Introductions</a>
+section of this document for details.</p>
+
+<p>Sending instances of other classes requires that you tell Banana how they
+should be serialized. <code>Referenceable</code> is good for
+copy-by-reference semantics<span class="footnote">In fact, if all you want is
+referenceability (and not callability), you can use <code class="API"
+base="foolscap.referenceable">OnlyReferenceable</code>. Strictly speaking,
+<code>Referenceable</code> is both <q>Referenceable</q> (meaning it is sent
+over the wire using pass-by-reference semantics, and it survives a round
+trip) and <q>Callable</q> (meaning you can invoke remote methods on it).
+<code>Referenceable</code> should really be named <code>Callable</code>, but
+the existing name has a lot of historical weight behind it.</span>. For
+copy-by-value semantics, the easiest route is to subclass <code class="API"
+base="foolscap.copyable">Copyable</code>. See the <a
+href="#copyable">Copyable</a> section for details. Note that you can also
+register an <code class="API" base="foolscap.copyable">ICopyable</code>
+adapter on third-party classes to avoid subclassing. You will need to
+register the <code>Copyable</code>'s name on the receiving end too, otherwise
+Banana will not know how to unserialize the incoming data stream.</p>
+
+<p>When returning a value from a remote method, you can do all these things,
+plus two more. If you raise an exception, the caller's Deferred will have the
+errback fired instead of the callback, with a <code class="API"
+base="foolscap.call">CopiedFailure</code> instance that describes what went
+wrong. The <code>CopiedFailure</code> is not quite as useful as a local <code
+class="API" base="twisted.python.failure">Failure</code> object would be: to
+send it over the wire, some contents are replaced with strings, and the
+actual Exception object (<code>f.value</code>) is replaced with its string
+representation. But you can still use it to find out what went wrong. The
+<code>CopiedFailure</code> may reveal more information about the internals of
+your program than you want: you can set the <code>unsafeTracebacks</code>
+flag on the Tub to limit outgoing <code>CopiedFailure</code>s to contain only
+the exception type (and none of the stack trace information that would reveal
+lines of your source code to the remote end).</p>
+
+<p>The other alternative is for your method to return a <code class="API"
+base="twisted.internet.defer">Deferred</code>. If this happens, the caller
+will not actually get a response until you fire that Deferred. This is useful
+when the remote operation being requested cannot complete right away. The
+caller's Deferred with fire with whatever value you eventually fire your own
+Deferred with. If your Deferred is errbacked, their Deferred will be
+errbacked with a <code>CopiedFailure</code>.</p>
+
+
+<h2>Constraints and RemoteInterfaces</h2><a name="constraints" />
+
+<p>One major feature introduced by Foolscap (relative to oldpb) is the
+serialization <code class="API" base="foolscap.schema">Constraint</code>.
+This lets you place limits on what kind of data you are willing to accept,
+which enables safer distributed programming. Typically python uses <q>duck
+typing</q>, wherein you usually just throw some arguments at the method and
+see what happens. When you are less sure of the origin of those arguments,
+you may want to be more circumspect. Enforcing type checking at the boundary
+between your code and the outside world may make it safer to use duck typing
+inside those boundaries. The type specifications also form a convenient
+remote API reference you can publish for prospective clients of your
+remotely-invokable service.</p>
+
+<p>In addition, these Constraints are enforced on each token as it arrives
+over the wire. This means that you can calculate a (small) upper bound on how
+much received data your program will store before it decides to hang up on
+the violator, minimizing your exposure to DoS attacks that involve sending
+random junk at you.</p>
+
+<p>There are three pieces you need to know about: Tokens, Constraints, and
+RemoteInterfaces.</p>
+
+<h3>Tokens</h3>
+
+<p>The fundamental unit of serialization is the Banana Token. These are
+thoroughly documented in the <a href="specifications/banana.xhtml">Banana
+Specification</a>, but what you need to know here is that each piece of
+non-container data, like a string or a number, is represented by a single
+token. Containers (like lists and dictionaries) are represented by a special
+OPEN token, followed by tokens for everything that is in the container,
+followed by the CLOSE token. Everything Banana does is in terms of these
+nested OPEN/stuff/stuff/CLOSE sequences of tokens.</p>
+
+<p>Each token consists of a header, a type byte, and an optional body. The
+header is always a base-128 number with a maximum of 64 digits, and the type
+byte is always a single byte. The body (if present) has a length dictated by
+the magnitude of the header.</p>
+
+<p>The length-first token format means that the receiving system never has to
+accept more than 65 bytes before it knows the type and size of the token, at
+which point it can make a decision about accepting or rejecting the rest of
+it.</p>
+
+<h3>Constraints</h3>
+
+<p>The schema <code>foolscap.schema</code> module has a variety of <code
+class="API" base="foolscap.schema">Constraint</code> classes that can be
+applied to incoming data. Most of them correspond to typical Python types,
+e.g. <code class="API" base="foolscap.schema">ListOf</code> matches a list,
+with a certain maximum length, and a child <code>Constraint</code> that gets
+applied to the contents of the list. You can nest <code>Constraint</code>s in
+this way to describe the <q>shape</q> of the object graph that you are
+willing to accept.</p>
+
+<p>At any given time, the receiving Banana protocol has single
+<code>Constraint</code> object that it enforces against the inbound data
+stream<span class="footnote">to be precise, each <code>Unslicer</code> on the
+receive stack has a <code>Constraint</code>, and the idea is that all of them
+get to pass judgement on the inbound token. A useful syntax to describe this
+sort of thing is still being worked out.</span>.</p>
+
+<h3>RemoteInterfaces</h3>
+
+<p>The <code class="API"
+base="foolscap.remoteinterface">RemoteInterface</code> is how you describe
+your constraints. You can provide a constraint for each argument of each
+method, as well as one for the return value. You can also specify addtional
+flags on the methods. The convention (which is actually enforced by the code)
+is to name <code>RemoteInterface</code> objects with an <q>RI</q> prefix,
+like <code>RIFoo</code>.</p>
+
+<p><code>RemoteInterfaces</code> are created and used a lot like the usual
+<code>zope.interface</code>-style <code>Interface</code>. They look like
+class definitions, inheriting from <code>RemoteInterface</code>. For each
+method, the default value of each argument is used to create a
+<code>Constraint</code> for that argument. Basic types (<code>int</code>,
+<code>str</code>, <code>bool</code>) are converted into a
+<code>Constraint</code> subclass (<code class="API"
+base="foolscap.schema">IntegerConstraint</code>, <code class="API"
+base="foolscap.schema">StringConstraint</code>, <code class="API"
+base="foolscap.schema">BooleanConstraint</code>). You can also use
+instances of other <code>Constraint</code> subclasses, like <code class="API"
+base="foolscap.schema">ListOf</code> and <code class="API"
+base="foolscap.schema">DictOf</code>. This <code>Constraint</code> will be
+enforced against the value for the given argument. Unless you specify
+otherwise, remote callers must match all the <code>Constraint</code>s you
+specify, all arguments listed in the RemoteInterface must be present, and no
+arguments outside that list will be accepted.</p>
+
+<p>Note that, like zope.interface, these methods should <b>not</b> include
+<q><code>self</code></q> in their argument list. This is because you are
+documenting how <em>other</em> people invoke your methods. <code>self</code>
+is an implementation detail. <code>RemoteInterface</code> will complain if
+you forget.</p>
+
+<p>The <q>methods</q> in a <code>RemoteInterface</code> should return a
+single value with the same format as the default arguments: either a basic
+type (<code>int</code>, <code>str</code>, etc) or a <code>Constraint</code>
+subclass. This <code>Constraint</code> is enforced on the return value of the
+method. If you are calling a method in somebody else's process, the argument
+constraints will be applied as a courtesy (<q>be conservative in what you
+send</q>), and the return value constraint will be applied to prevent the
+server from doing evil things to you. If you are running a method on behalf
+of a remote client, the argument constraints will be enforced to protect
+<em>you</em>, while the return value constraint will be applied as a
+courtesy.</p>
+
+<p>Attempting to send a value that does not satisfy the Constraint will
+result in a <code class="API" base="foolscap">Violation</code> exception
+being raised.</p>
+
+<p>You can also specify methods by defining attributes of the same name in
+the <code>RemoteInterface</code> object. Each attribute value should be an
+instance of <code class="API"
+base="foolscap.schema">RemoteMethodSchema</code><span
+class="footnote">although technically it can be any object which implements
+the <code class="API" base="foolscap.schema">IRemoteMethodConstraint</code>
+interface</span>. This approach is more flexible: there are some constraints
+that are not easy to express with the default-argument syntax, and this is
+the only way to set per-method flags. Note that all such method-defining
+attributes must be set in the <code>RemoteInterface</code> body itself,
+rather than being set on it after the fact (i.e. <code>RIFoo.doBar =
+stuff</code>). This is required because the <code>RemoteInterface</code>
+metaclass magic processes all of these attributes only once, immediately
+after the <code>RemoteInterface</code> body has been evaluated.</p>
+
+<p>The <code>RemoteInterface</code> <q>class</q> has a name. Normally this is
+the (short) classname<span
+class="footnote"><code>RIFoo.__class__.__name__</code>, if
+<code>RemoteInterface</code>s were actually classes, which they're
+not</span>. You can override this
+name by setting a special <code>__remote_name__</code> attribute on the
+<code>RemoteInterface</code> (again, in the body). This name is important
+because it is externally visible: all <code>RemoteReference</code>s that
+point at your <code>Referenceable</code>s will remember the name of the
+<code>RemoteInterface</code>s it implements. This is what enables the
+type-checking to be performed on both ends of the wire.</p>
+
+<p>In the future, this ought to default to the <b>fully-qualified</b>
+classname (like <code>package.module.RIFoo</code>), so that two
+RemoteInterfaces with the same name in different modules can co-exist. In the
+current release, these two RemoteInterfaces will collide (and provoke an
+import-time error message complaining about the duplicate name). As a result,
+if you have such classes (e.g. <code>foo.RIBar</code> and
+<code>baz.RIBar</code>), you <b>must</b> use <code>__remote_name__</code> to
+distinguish them (by naming one of them something other than
+<code>RIBar</code> to avoid this error.
+
+Hopefully this will be improved in a future version, but it looks like a
+difficult change to implement, so the standing recommendation is to use
+<code>__remote_name__</code> on all your RemoteInterfaces, and set it to a
+suitably unique string (like a URI).</p>
+
+<p>Here's an example:</p>
+
+<pre class="python">
+from foolscap import RemoteInterface, schema
+
+class RIMath(RemoteInterface):
+ __remote_name__ = "RIMath.using-foolscap.docs.foolscap.twistedmatrix.com"
+ def add(a=int, b=int):
+ return int
+ # declare it with an attribute instead of a function definition
+ subtract = schema.RemoteMethodSchema(a=int, b=int, _response=int)
+ def sum(args=schema.ListOf(int)):
+ return int
+</pre>
+
+
+<h3>Using RemoteInterface</h3>
+
+<p>To declare that your <code>Referenceable</code> responds to a particular
+<code>RemoteInterface</code>, use the normal <code>implements()</code>
+annotation:</p>
+
+<pre class="python">
+class MathServer(foolscap.Referenceable):
+ implements(RIMath)
+
+ def remote_add(self, a, b):
+ return a+b
+ def remote_subtract(self, a, b):
+ return a-b
+ def remote_sum(self, args):
+ total = 0
+ for a in args: total += a
+ return total
+</pre>
+
+<p>To enforce constraints everywhere, both sides will need to know about the
+<code>RemoteInterface</code>, and both must know it by the same name. It is a
+good idea to put the <code>RemoteInterface</code> in a common file that is
+imported into the programs running on both sides. It is up to you to make
+sure that both sides agree on the interface. Future versions of Foolscap may
+implement some sort of checksum-verification or Interface-serialization as a
+failsafe, but fundamentally the <code>RemoteInterface</code> that
+<em>you</em> are using defines what <em>your</em> program is prepared to
+handle. There is no difference between an old client accidentaly using a
+different version of the RemoteInterface by mistake, and a malicious attacker
+actively trying to confuse your code. The only promise that Foolscap can make
+is that the constraints you provide in the RemoteInterface will be faithfully
+applied to the incoming data stream, so that you don't need to do the type
+checking yourself inside the method.</p>
+
+<p>When making a remote method call, you use the <code>RemoteInterface</code>
+to identify the method instead of a string. This scopes the method name to
+the RemoteInterface:</p>
+
+<pre class="python">
+d = remote.callRemote(RIMath["add"], a=1, b=2)
+# or
+d = remote.callRemote(RIMath["add"], 1, 2)
+</pre>
+
+<h2>Pass-By-Copy</h2>
+
+<p>You can pass (nearly) arbitrary instances over the wire. Foolscap knows
+how to serialize all of Python's native data types already: numbers, strings,
+unicode strings, booleans, lists, tuples, dictionaries, sets, and the None
+object. You can teach it how to serialize instances of other types too.
+Foolscap will not serialize (or deserialize) any class that you haven't
+taught it about, both for security and because it refuses the temptation to
+guess your intentions about how these unknown classes ought to be
+serialized.</p>
+
+<p>The simplest possible way to pass things by copy is demonstrated in the
+following code fragment:</p>
+
+<pre class="python">
+from foolscap import Copyable, RemoteCopy
+
+class MyPassByCopy(Copyable, RemoteCopy):
+ typeToCopy = copytype = "MyPassByCopy"
+ def __init__(self):
+ # RemoteCopy subclasses may not accept any __init__ arguments
+ pass
+ def setCopyableState(self, state):
+ self.__dict__ = state
+</pre>
+
+<p>If the code on both sides of the wire import this class, then any
+instances of <code>MyPassByCopy</code> that are present in the arguments of a
+remote method call (or returned as the result of a remote method call) will
+be serialized and reconstituted into an equivalent instance on the other
+side.</p>
+
+<p>For more complicated things to do with pass-by-copy, see the documentation
+on <a href="copyable.html">Copyable</a>. This explains the difference between
+<code>Copyable</code> and <code>RemoteCopy</code>, how to control the
+serialization and deserialization process, and how to arrange for
+serialization of third-party classes that are not subclasses of
+<code>Copyable</code>.</p>
+
+
+<h2>Third-party References</h2><a name="introductions" />
+
+<p>Another new feature of Foolscap is the ability to send
+<code>RemoteReference</code>s to third parties. The classic scenario for this
+is illustrated by the <a
+href="http://www.erights.org/elib/capability/overview.html">three-party
+Granovetter diagram</a>. One party (Alice) has RemoteReferences to two other
+objects named Bob and Carol. She wants to share her reference to Carol with
+Bob, by including it in a message she sends to Bob (i.e. by using it as an
+argument when she invokes one of Bob's remote methods). The Foolscap code for
+doing this would look like:</p>
+
+<pre class="python">
+bobref.callRemote("foo", intro=carolref)
+</pre>
+
+<p>When Bob receives this message (i.e. when his <code>remote_foo</code>
+method is invoked), he will discover that he's holding a fully-functional
+<code>RemoteReference</code> to the object named Carol<span
+class="footnote">and if everyone involved is using authenticated Tubs, then
+Foolscap offers a guarantee, in the cryptographic sense, that Bob will wind
+up with a reference to the same object that Alice intended. The authenticated
+FURLs prevent DNS-spoofing and man-in-the-middle attacks.</span>. He can
+start using this RemoteReference right away:</p>
+
+<pre class="python">
+class Bob(foolscap.Referenceable):
+ def remote_foo(self, intro):
+ self.carol = intro
+ carol.callRemote("howdy", msg="Pleased to meet you", you=intro)
+ return carol
+</pre>
+
+<p>If Bob sends this <code>RemoteReference</code> back to Alice, her method
+will see the same <code>RemoteReference</code> that she sent to Bob. In this
+example, Bob sends the reference by returning it from the original
+<code>remote_foo</code> method call, but he could almost as easily send it in
+a separate method call.</p>
+
+<pre class="python">
+class Alice(foolscap.Referenceable):
+ def start(self, carol):
+ self.carol = carol
+ d = self.bob.callRemote("foo", intro=carol)
+ d.addCallback(self.didFoo)
+ def didFoo(self, result):
+ assert result is self.carol # this will be true
+</pre>
+
+<p>Moreover, if Bob sends it back to <em>Carol</em> (completing the
+three-party round trip), Carol will see it as her original
+<code>Referenceable</code>.</p>
+
+<pre class="python">
+class Carol(foolscap.Referenceable):
+ def remote_howdy(self, msg, you):
+ assert you is self # this will be true
+</pre>
+
+<p>In addition to this, in the four-party introduction sequence as used by
+the <a
+href="http://www.erights.org/elib/equality/grant-matcher/index.html">Grant
+Matcher Puzzle</a>, when a Referenceable is sent to the same destination
+through multiple paths, the recipient will receive the same
+<code>RemoteReference</code> object from both sides.</p>
+
+<p>For a <code>RemoteReference</code> to be transferrable to third-parties in
+this fashion, the original <code>Referenceable</code> must live in a Tub
+which has a working listening port, and an established base URL. It is not
+necessary for the Referenceable to have been published with
+<code>registerReference</code> first: if it is sent over the wire before a
+name has been associated with it, it will be registered under a new random
+and unguessable name. The <code>RemoteReference</code> will contain the
+resulting URL, enabling it to be sent to third parties.</p>
+
+<p>When this introduction is made, the receiving system must establish a
+connection with the Tub that holds the original Referenceable, and acquire
+its own RemoteReference. These steps must take place before the remote method
+can be invoked, and other method calls might arrive before they do. All
+subsequent method calls are queued until the one that involved the
+introduction is performed. Foolscap guarantees (by default) that the messages
+sent to a given Referenceable will be delivered in the same order. In the
+future there may be options to relax this guarantee, in exchange for higher
+performance, reduced memory consumption, multiple priority queues, limited
+latency, or other features. There might even be an option to turn off
+introductions altogether.</p>
+
+<p>Also note that enabling this capability means any of your communication
+peers can make you create TCP connections to hosts and port numbers of their
+choosing. The fact that those connections can only speak the Foolscap
+protocol may reduce the security risk presented, but it still lets other
+people be annoying.</p>
+
+
+</body></html>
+++ /dev/null
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>Introduction to Foolscap</title>
-<style src="stylesheet-unprocessed.css"></style>
-</head>
-
-<body>
-<h1>Introduction to Foolscap</h1>
-
-<h2>Introduction</h2>
-
-<p>Suppose you find yourself in control of both ends of the wire: you have
-two programs that need to talk to each other, and you get to use any protocol
-you want. If you can think of your problem in terms of objects that need to
-make method calls on each other, then chances are good that you can use the
-Foolscap protocol rather than trying to shoehorn your needs into something
-like HTTP, or implementing yet another RPC mechanism.</p>
-
-<p>Foolscap is based upon a few central concepts:</p>
-
-<ul>
-
- <li><em>serialization</em>: taking fairly arbitrary objects and types,
- turning them into a chunk of bytes, sending them over a wire, then
- reconstituting them on the other end. By keeping careful track of object
- ids, the serialized objects can contain references to other objects and the
- remote copy will still be useful. </li>
-
- <li><em>remote method calls</em>: doing something to a local proxy and
- causing a method to get run on a distant object. The local proxy is called
- a <code class="API" base="foolscap.referenceable">RemoteReference</code>,
- and you <q>do something</q> by running its <code>.callRemote</code> method.
- The distant object is called a <code class="API"
- base="foolscap.referenceable">Referenceable</code>, and it has methods like
- <code>remote_foo</code> that will be invoked.</li>
-
-</ul>
-
-<p>Foolscap is the descendant of Perspective Broker (which lived in the
-twisted.spread package). For many years it was known as "newpb". A lot of the
-API still has the name "PB" in it somewhere. These will probably go away
-sooner or later.</p>
-
-<p>A "foolscap" is a size of paper, probably measuring 17 by 13.5 inches. A
-twisted foolscap of paper makes a good fool's cap. Also, "cap" makes me think
-of capabilities, and Foolscap is a protocol to implement a distributed
-object-capabilities model in python.</p>
-
-
-<h2>Getting Started</h2>
-
-<p>Any Foolscap application has at least two sides: one which hosts a
-remotely-callable object, and another which calls (remotely) the methods of
-that object. We'll start with a simple example that demostrates both ends.
-Later, we'll add more features like RemoteInterface declarations, and
-transferring object references.</p>
-
-<p>The most common way to make an object with remotely-callable methods is to
-subclass <code class="API"
-base="foolscap.referenceable">Referenceable</code>. Let's create a simple
-server which does basic arithmetic. You might use such a service to perform
-difficult mathematical operations, like addition, on a remote machine which
-is faster and more capable than your own<span class="footnote">although
-really, if your client machine is too slow to perform this kind of math, it
-is probably too slow to run python or use a network, so you should seriously
-consider a hardware upgrade</span>.</p>
-
-<pre class="python">
-from foolscap import Referenceable
-
-class MathServer(Referenceable):
- def remote_add(self, a, b):
- return a+b
- def remote_subtract(self, a, b):
- return a-b
- def remote_sum(self, args):
- total = 0
- for a in args: total += a
- return total
-
-myserver = MathServer()
-</pre>
-
-<p>On the other end of the wire (which you might call the <q>client</q>
-side), the code will have a <code class="API"
-base="foolscap.referenceable">RemoteReference</code> to this object. The
-<code>RemoteReference</code> has a method named <code class="API"
-base="foolscap.referenceable.RemoteReference">callRemote</code> which you
-will use to invoke the method. It always returns a Deferred, which will fire
-with the result of the method. Assuming you've already acquired the
-<code>RemoteReference</code>, you would invoke the method like this:</p>
-
-<pre class="python">
-def gotAnswer(result):
- print "result is", result
-def gotError(err):
- print "error:", err
-d = remote.callRemote("add", 1, 2)
-d.addCallbacks(gotAnswer, gotError)
-</pre>
-
-<p>Ok, now how do you acquire that <code>RemoteReference</code>? How do you
-make the <code>Referenceable</code> available to the outside world? For this,
-we'll need to discuss the <q>Tub</q>, and the concept of a <q>PB URL</q>.</p>
-
-<h2>Tubs: The Foolscap Service</h2>
-
-<p>The <code class="API" base="foolscap.pb">Tub</code> is the container that
-you use to publish <code>Referenceable</code>s, and is the middle-man you use
-to access <code>Referenceable</code>s on other systems. It is known as the
-<q>Tub</q>, since it provides similar naming and identification properties as
-the <a href="http://www.erights.org/">E language</a>'s <q>Vat</q><span
-class="footnote">but they do not provide quite the same insulation against
-other objects as E's Vats do. In this sense, Tubs are leaky Vats.</span>. If
-you want to make a <code>Referenceable</code> available to the world, you
-create a Tub, tell it to listen on a TCP port, and then register the
-<code>Referenceable</code> with it under a name of your choosing. If you want
-to access a remote <code>Referenceable</code>, you create a Tub and ask it to
-acquire a <code>RemoteReference</code> using that same name.</p>
-
-<p>The <code>Tub</code> is a Twisted <code class="API"
-base="twisted.application.service">Service</code> subclass, so you use it in
-the same way: once you've created one, you attach it to a parent Service or
-Application object. Once the top-level Application object has been started,
-the Tub will start listening on any network ports you've requested. When the
-Tub is shut down, it will stop listening and drop any connections it had
-established since last startup. If you have no parent to attach it to, you
-can use <code>startService</code> and <code>stopService</code> on the Tub
-directly.</p>
-
-<p>Note that you must start the Tub before calling <code>getReference</code>
-or <code>connectTo</code>, since both of these trigger network activity, and
-Tubs are supposed to be silent until they are started. In a future release
-this requirement may be relaxed, but the principle of "no network activity
-until the Tub is started" will be maintained, probably by queueing the
-<code>getReference</code> calls and handling them after the Tub been
-started.</p>
-
-<h3>Making your Tub remotely accessible</h3>
-
-<p>To make any of your <code>Referenceable</code>s available, you must make
-your Tub available. There are three parts: give it an identity, have it
-listen on a port, and tell it the protocol/hostname/portnumber at which that
-port is accessibly to the outside world.</p>
-
-<p>In general, the Tub will generate its own identity, the <em>TubID</em>, by
-creating an SSL private key certificate and hashing it into a suitably-long
-random-looking string. This is the primary identifier of the Tub: everything
-else is just a <em>location hint</em> that suggests how the Tub might be
-reached. The fact that the TubID is tied to the private key allows PB URLs to
-be <q>secure</q> references (meaning that no third party can cause you to
-connect to the wrong reference). You can also create a Tub with a
-pre-existing certificate, which is how Tubs can retain a persistent identity
-over multiple executions.</p>
-
-<p>You can also create an <code>UnauthenticatedTub</code>, which has an empty
-TubID. Hosting and connecting to unauthenticated Tubs do not require the
-pyOpenSSL library, but do not provide privacy, authentication, connection
-redirection, or shared listening ports. The PB-URLs that point to
-unauthenticated Tubs have a distinct form (starting with <code>pbu:</code>
-instead of <code>pb:</code>) to make sure they are not mistaken for
-authenticated Tubs. PB uses authenticated Tubs by default.</p>
-
-<p>Having the Tub listen on a TCP port is as simple as calling <code
-class="API" base="foolscap.pb.Tub">listenOn</code> with a <code class="API"
-base="twisted.application">strports</code>-formatted port specification
-string. The simplest such string would be <q>tcp:12345</q>, to listen on port
-12345 on all interfaces. Using <q>tcp:12345:interface=127.0.0.1</q> would
-cause it to only listen on the localhost interface, making it available only
-to other processes on the same host. The <code>strports</code> module
-provides many other possibilities.</p>
-
-<p>The Tub needs to be told how it can be reached, so it knows what host and
-port to put into the URLs it creates. This location is simply a string in the
-format <q>host:port</q>, using the host name by which that TCP port you've
-just opened can be reached. Foolscap cannot, in general, guess what this name
-is, especially if there are NAT boxes or port-forwarding devices in the way.
-If your machine is reachable directly over the internet as
-<q>myhost.example.com</q>, then you could use something like this:</p>
-
-<pre class="python">
-from foolscap import Tub
-
-tub = Tub()
-tub.listenOn("tcp:12345") # start listening on TCP port 12345
-tub.setLocation("myhost.example.com:12345")
-</pre>
-
-<h3>Registering the Referenceable</h3>
-
-<p>Once the Tub has a Listener and a location, you can publish your
-<code>Referenceable</code> to the entire world by picking a name and
-registering it:</p>
-
-<pre class="python">
-url = tub.registerReference(myserver, "math-service")
-</pre>
-
-<p>This returns the <q>PB URL</q> for your <code>Referenceable</code>. Remote
-systems will use this URL to access your newly-published object. The
-registration just maps a per-Tub name to the <code>Referenceable</code>:
-technically the same <code>Referenceable</code> could be published multiple
-times, under different names, or even be published by multiple Tubs in the
-same application. But in general, each program will have exactly one Tub, and
-each object will be registered under only one name.</p>
-
-<p>In this example (if we pretend the generated TubID was <q>ABCD</q>), the
-URL returned by <code>registerReference</code> would be
-<code>"pb://ABCD@myhost.example.com:12345/math-service"</code>.</p>
-
-<p>If you do not provide a name, a random (and unguessable) name will be
-generated for you. This is useful when you want to give access to your
-<code>Referenceable</code> to someone specific, but do not want to make it
-possible for someone else to acquire it by guessing the name.</p>
-
-<p>To use an unauthenticated Tub instead, you would do the following:</p>
-<pre class="python">
-from foolscap import UnauthenticatedTub
-
-tub = UnauthenticatedTub()
-tub.listenOn("tcp:12345") # start listening on TCP port 12345
-tub.setLocation("myhost.example.com:12345")
-url = tub.registerReference(myserver, "math-service")
-</pre>
-
-<p>In this case, the URL would be
-<code>"pbu://myhost.example.com:12345/math-service"</code>. The deterministic
-nature of this form makes it slightly easier to throw together
-quick-and-dirty Foolscap applications, since you only need to hard-code the
-target host and port into the client side program. However any serious
-application should just used the default authenticated form and use a full
-URL as their starting point. Note that the URL can come from anywhere: typed
-in by the user, retrieved from a web page, or hardcoded into the
-application.</p>
-
-<h4>Using a persistent certificate</h4>
-
-<p>The Tub uses a TLS private-key certificate as the base of all its
-cryptographic operations. If you don't give it one when you create the Tub,
-it will generate a brand-new one.</p>
-
-<p>The TubID is simply the hash of this certificate, so if you are writing an
-application that should have a stable long-term identity, you will need to
-insure that the Tub uses the same certificate every time your app starts. The
-easiest way to do this is to pass the <code>certFile=</code> argument into
-your <code>Tub()</code> constructor call. This argument provides a filename
-where you want the Tub to store its certificate. The first time the Tub is
-started (when this file does not exist), the Tub will generate a new
-certificate and store it here. On subsequent invocations, the Tub will read
-the earlier certificate from this location. Make sure this filename points to
-a writable location, and that you pass the same filename to
-<code>Tub()</code> each time.</p>
-
-<h3>Retrieving a RemoteReference</h3>
-
-<p>On the <q>client</q> side, you also need to create a Tub, although you
-don't need to perform the (<code>listenOn</code>, <code>setLocation</code>,
-<code>registerReference</code>) sequence unless you are also publishing
-<code>Referenceable</code>s to the world. To acquire a reference to somebody
-else's object, just use <code class="API"
-base="foolscap.pb.Tub">getReference</code>:</p>
-
-<pre class="python">
-from foolscap import Tub
-
-tub = Tub()
-tub.startService()
-d = tub.getReference("pb://ABCD@myhost.example.com:12345/math-service")
-def gotReference(remote):
- print "Got the RemoteReference:", remote
-def gotError(err):
- print "error:", err
-d.addCallbacks(gotReference, gotError)
-</pre>
-
-<p><code>getReference</code> returns a Deferred which will fire with a
-<code>RemoteReference</code> that is connected to the remote
-<code>Referenceable</code> named by the URL. It will use an existing
-connection, if one is available, and it will return an existing
-<code>RemoteReference</code>, it one has already been acquired.</p>
-
-<h3>Complete example</h3>
-
-<p>Here are two programs, one implementing the server side of our
-remote-addition protocol, the other behaving as a client. This first example
-uses an unauthenticated Tub so you don't have to manually copy a URL from the
-server to the client. Both of these are standalone programs (you just run
-them), but normally you would create an <code class="API"
-base="twisted.application.service">Application</code> object and pass the
-file to <code>twistd -noy</code>. An example of that usage will be provided
-later.</p>
-
-<a href="listings/pb1server.py" class="py-listing"
-skipLines="2">pb1server.py</a>
-
-<a href="listings/pb1client.py" class="py-listing"
-skipLines="2">pb1client.py</a>
-
-<pre class="shell">
-% doc/listings/pb1server.py
-the object is available at: pbu://localhost:12345/math-service
-</pre>
-
-<pre class="shell">
-% doc/listings/pb1client.py
-got a RemoteReference
-asking it to add 1+2
-the answer is 3
-%
-</pre>
-
-<p>The second example uses authenticated Tubs. When running this example, you
-must copy the URL printed by the server and provide it as an argument to the
-client.</p>
-
-<a href="listings/pb2server.py" class="py-listing"
-skipLines="2">pb2server.py</a>
-
-<a href="listings/pb2client.py" class="py-listing"
-skipLines="2">pb2client.py</a>
-
-<pre class="shell">
-% doc/listings/pb2server.py
-the object is available at: pb://abcd123@localhost:12345/math-service
-</pre>
-
-<pre class="shell">
-% doc/listings/pb2client.py pb://abcd123@localhost:12345/math-service
-got a RemoteReference
-asking it to add 1+2
-the answer is 3
-%
-</pre>
-
-
-<h3>PB URLs</h3>
-
-<p>In Foolscap, each world-accessible Referenceable has one or more URLs
-which are <q>secure</q>, where we use the capability-security definition of
-the term, meaning those URLs have the following properties:</p>
-
-<ul>
- <li>The only way to acquire the URL is either to get it from someone else
- who already has it, or to be the person who published it in the first
- place.</li>
-
- <li>Only that original creator of the URL gets to determine which
- Referenceable it will connect to. If your
- <code>tub.getReference(url)</code> call succeeds, the Referenceable you
- will be connected to will be the right one.</li>
-</ul>
-
-<p>To accomplish the first goal, PB URLs must be unguessable. You can
-register the reference with a human-readable name if your intention is to
-make it available to the world, but in general you will let
-<code>tub.registerReference</code> generate a random name for you, preserving
-the unguessability property.</p>
-
-<p>To accomplish the second goal, the cryptographically-secure TubID is used
-as the primary identifier, and the <q>location hints</q> are just that:
-hints. If DNS has been subverted to point the hostname at a different
-machine, or if a man-in-the-middle attack causes you to connect to the wrong
-box, the TubID will not match the remote end, and the connection will be
-dropped. These attacks can cause a denial-of-service, but they cannot cause
-you to mistakenly connect to the wrong target.</p>
-
-<p>Obviously this second property only holds if you use SSL. If you choose to
-use unauthenticated Tubs, all security properties are lost.</p>
-
-<p>The format of a PB URL, like
-<code>pb://abcd123@example.com:5901,backup.example.com:8800/math-server</code>,
-is as follows<span class="footnote">note that the PB URL uses the same format
-as an <a href="http://www.waterken.com/dev/YURL/httpsy/">HTTPSY</a>
-URL</span>:</p>
-
-<ol>
- <li>The literal string <code>pb://</code></li>
- <li>The TubID (as a base32-encoded hash of the SSL certificate)</li>
- <li>A literal <code>@</code> sign</li>
-
- <li>A comma-separated list of <q>location hints</q>. Each is one of the
- following:
- <ul>
- <li>TCP over IPv4 via DNS: <code>HOSTNAME:PORTNUM</code></li>
- <li>TCP over IPv4 without DNS: <code>A.B.C.D:PORTNUM</code></li>
- <li>TCP over IPv6: (TODO, maybe <code>tcp6:HOSTNAME:PORTNUM</code> ?</li>
- <li>TCP over IPv6 w/o DNS: (TODO,
- maybe <code>tcp6:[X:Y::Z]:PORTNUM</code></li>
- <li>Unix-domain socket: (TODO)</li>
- </ul>
-
- Each location hint is attempted in turn. Servers can return a
- <q>redirect</q>, which will cause the client to insert the provided
- redirect targets into the hint list and start trying them before continuing
- with the original list.</li>
-
- <li>A literal <code>/</code> character</li>
- <li>The reference's name</li>
-</ol>
-
-<p>(Unix-domain sockets are represented with only a single location hint, in
-the format <code>pb://ABCD@unix/path/to/socket/NAME</code>, but this needs
-some work)</p>
-
-<p>PB URLs for unauthenticated Tubs, like
-<code>pbu://example.com:8700/math-server</code>, are formatted as
-follows:</p>
-
-<ol>
- <li>The literal string <code>pbu://</code></li>
- <li>A single location hint</li>
- <li>A literal <code>/</code> character</li>
- <li>The reference's name</li>
-</ol>
-
-<h2>Clients vs Servers, Names and Capabilities</h2>
-
-<p>It is worthwhile to point out that Foolscap is a symmetric protocol.
-<code>Referenceable</code> instances can live on either side of a wire, and
-the only difference between <q>client</q> and <q>server</q> is who publishes
-the object and who initiates the network connection.</p>
-
-<p>In any Foolscap-using system, the very first object exchanged must be
-acquired with a <code>tub.getReference(url)</code> call<span
-class="footnote">in fact, the very <em>very</em> first object exchanged is a
-special implicit RemoteReference to the remote Tub itself, which implements
-an internal protocol that includes a method named
-<code>remote_getReference</code>. The <code>tub.getReference(url)</code> call
-is turned into one step that connects to the remote Tub, and a second step
-which invokes remotetub.callRemote("getReference", refname) on the
-result</span>, which means it must have been published with a call to
-<code>tub.registerReference(ref, name)</code>. After that, other objects can
-be passed as an argument to (or a return value from) a remotely-invoked
-method of that first object. Any suitable <code>Referenceable</code> object
-that is passed over the wire will appear on the other side as a corresponding
-<code>RemoteReference</code>. It is not necessary to
-<code>registerReference</code> something to let it pass over the wire.</p>
-
-<p>The converse of this property is thus: if you do <em>not</em>
-<code>registerReference</code> a particular <code>Referenceable</code>, and
-you do <em>not</em> give it to anyone else (by passing it in an argument to
-somebody's remote method, or return it from one of your own), then nobody
-else will be able to get access to that <code>Referenceable</code>. This
-property means the <code>Referenceable</code> is a <q>capability</q>, as
-holding a corresponding <code>RemoteReference</code> gives someone a power
-that they cannot acquire in any other way<span class="footnote">of course,
-the Foolscap connections must be secured with SSL (otherwise an eavesdropper
-or man-in-the-middle could get access), and the registered name must be
-unguessable (or someone else could acquire a reference), but both of these
-are the default.</span></p>
-
-<p>In the following example, the first program creates an RPN-style
-<code>Calculator</code> object which responds to <q>push</q>, <q>pop</q>,
-<q>add</q>, and <q>subtract</q> messages from the user. The user can also
-register an <code>Observer</code>, to which the Calculator sends an
-<code>event</code> message each time something happens to the calculator's
-state. When you consider the <code>Calculator</code> object, the first
-program is the server and the second program is the client. When you think
-about the <code>Observer</code> object, the first program is a client and the
-second program is the server. It also happens that the first program is
-listening on a socket, while the second program initiated a network
-connection to the first. It <em>also</em> happens that the first program
-published an object under some well-known name, while the second program has
-not published any objects. These are all independent properties.</p>
-
-<p>Also note that the Calculator side of the example is implemented using a
-<code class="API" base="twisted.application.service">Application</code>
-object, which is the way you'd normally build a real-world application. You
-therefore use <code>twistd</code> to launch the program. The User side is
-written with the same <code>reactor.run()</code> style as the earlier
-example.</p>
-
-<p>The server registers the Calculator instance and prints the PBURL at which
-it is listening. You need to pass this PBURL to the client program so it
-knows how to contact the servre. If you have a modern version of Twisted (2.5
-or later) and the right encryption libraries installed, you'll get an
-authenticated Tub (for which the PBURL will start with "pb:" and will be
-fairly long). If you don't, you'll get an unauthenticated Tub (with a
-relatively short PBURL that starts with "pbu:").</p>
-
-<a href="listings/pb3calculator.py" class="py-listing"
-skipLines="2">pb3calculator.py</a>
-
-<a href="listings/pb3user.py" class="py-listing"
-skipLines="2">pb3user.py</a>
-
-<pre class="shell">
-% twistd -noy doc/listings/pb3calculator.py
-15:46 PDT [-] Log opened.
-15:46 PDT [-] twistd 2.4.0 (/usr/bin/python 2.4.4) starting up
-15:46 PDT [-] reactor class: twisted.internet.selectreactor.SelectReactor
-15:46 PDT [-] Loading doc/listings/pb3calculator.py...
-15:46 PDT [-] the object is available at:
- pb://5ojw4cv4u4d5cenxxekjukrogzytnhop@localhost:12345/calculator
-15:46 PDT [-] Loaded.
-15:46 PDT [-] foolscap.pb.Listener starting on 12345
-15:46 PDT [-] Starting factory <Listener at 0x4869c0f4 on tcp:12345
- with tubs None>
-</pre>
-
-<pre class="shell">
-% doc/listings/pb3user.py \
- pb://5ojw4cv4u4d5cenxxekjukrogzytnhop@localhost:12345/calculator
-event: push(2)
-event: push(3)
-event: add
-event: pop
-the result is 5
-%
-</pre>
-
-
-<h2>Invoking Methods, Method Arguments</h2>
-
-<p>As you've probably already guessed, all the methods with names that begin
-with <code>remote_</code> will be available to anyone who manages to acquire
-a corresponding <code>RemoteReference</code>. <code>remote_foo</code> matches
-a <code>ref.callRemote("foo")</code>, etc. This name lookup can be changed by
-overriding <code>Referenceable</code> (or, perhaps more usefully,
-implementing an <code class="API"
-base="foolscap.ipb">IRemotelyCallable</code> adapter).</p>
-
-<p>The arguments of a remote method may be passed as either positional
-parameters (<code>foo(1,2)</code>), or as keyword args
-(<code>foo(a=1,b=2)</code>), or a mixture of both. The usual python rules
-about not duplicating parameters apply.</p>
-
-<p>You can pass all sorts of normal objects to a remote method: strings,
-numbers, tuples, lists, and dictionaries. The serialization of these objects
-is handled by <a href="specifications/banana.xhtml">Banana</a>, which knows
-how to convey arbitrary object graphs over the wire. Things like containers
-which contain multiple references to the same object, and recursive
-references (cycles in the object graph) are all handled correctly<span
-class="footnote">you may not want to accept shared objects in your method
-arguments, as it could lead to surprising behavior depending upon how you
-have written your method. The <code class="API"
-base="foolscap.schema">Shared</code> constraint will let you express this,
-and is described in the <a href="#constraints">Constraints</a> section of
-this document</span>.</p>
-
-<p>Passing instances is handled specially. Foolscap will not send anything
-over the wire that it does not know how to serialize, and (unlike the
-standard <code>pickle</code> module) it will not make assumptions about how
-to handle classes that that have not been explicitly marked as serializable.
-This is for security, both for the sender (making you you don't pass anything
-over the wire that you didn't intend to let out of your security perimeter),
-and for the recipient (making sure outsiders aren't allowed to create
-arbitrary instances inside your memory space, and therefore letting them run
-somewhat arbitrary code inside <em>your</em> perimeter).</p>
-
-<p>Sending <code>Referenceable</code>s is straightforward: they always appear
-as a corresponding <code>RemoteReference</code> on the other side. You can
-send the same <code>Referenceable</code> as many times as you like, and it
-will always show up as the same <code>RemoteReference</code> instance. A
-distributed reference count is maintained, so as long as the remote side
-hasn't forgotten about the <code>RemoteReference</code>, the original
-<code>Referenceable</code> will be kept alive.</p>
-
-<p>Sending <code>RemoteReference</code>s fall into two categories. If you are
-sending a <code>RemoteReference</code> back to the Tub that you got it from,
-they will see their original <code>Referenceable</code>. If you send it to
-some other Tub, they will (eventually) see a <code>RemoteReference</code> of
-their own. This last feature is called an <q>introduction</q>, and has a few
-additional requirements: see the <a href="#introductions">Introductions</a>
-section of this document for details.</p>
-
-<p>Sending instances of other classes requires that you tell Banana how they
-should be serialized. <code>Referenceable</code> is good for
-copy-by-reference semantics<span class="footnote">In fact, if all you want is
-referenceability (and not callability), you can use <code class="API"
-base="foolscap.referenceable">OnlyReferenceable</code>. Strictly speaking,
-<code>Referenceable</code> is both <q>Referenceable</q> (meaning it is sent
-over the wire using pass-by-reference semantics, and it survives a round
-trip) and <q>Callable</q> (meaning you can invoke remote methods on it).
-<code>Referenceable</code> should really be named <code>Callable</code>, but
-the existing name has a lot of historical weight behind it.</span>. For
-copy-by-value semantics, the easiest route is to subclass <code class="API"
-base="foolscap.copyable">Copyable</code>. See the <a
-href="#copyable">Copyable</a> section for details. Note that you can also
-register an <code class="API" base="foolscap.copyable">ICopyable</code>
-adapter on third-party classes to avoid subclassing. You will need to
-register the <code>Copyable</code>'s name on the receiving end too, otherwise
-Banana will not know how to unserialize the incoming data stream.</p>
-
-<p>When returning a value from a remote method, you can do all these things,
-plus two more. If you raise an exception, the caller's Deferred will have the
-errback fired instead of the callback, with a <code class="API"
-base="foolscap.call">CopiedFailure</code> instance that describes what went
-wrong. The <code>CopiedFailure</code> is not quite as useful as a local <code
-class="API" base="twisted.python.failure">Failure</code> object would be: to
-send it over the wire, some contents are replaced with strings, and the
-actual Exception object (<code>f.value</code>) is replaced with its string
-representation. But you can still use it to find out what went wrong. The
-<code>CopiedFailure</code> may reveal more information about the internals of
-your program than you want: you can set the <code>unsafeTracebacks</code>
-flag on the Tub to limit outgoing <code>CopiedFailure</code>s to contain only
-the exception type (and none of the stack trace information that would reveal
-lines of your source code to the remote end).</p>
-
-<p>The other alternative is for your method to return a <code class="API"
-base="twisted.internet.defer">Deferred</code>. If this happens, the caller
-will not actually get a response until you fire that Deferred. This is useful
-when the remote operation being requested cannot complete right away. The
-caller's Deferred with fire with whatever value you eventually fire your own
-Deferred with. If your Deferred is errbacked, their Deferred will be
-errbacked with a <code>CopiedFailure</code>.</p>
-
-
-<h2>Constraints and RemoteInterfaces</h2><a name="constraints" />
-
-<p>One major feature introduced by Foolscap (relative to oldpb) is the
-serialization <code class="API" base="foolscap.schema">Constraint</code>.
-This lets you place limits on what kind of data you are willing to accept,
-which enables safer distributed programming. Typically python uses <q>duck
-typing</q>, wherein you usually just throw some arguments at the method and
-see what happens. When you are less sure of the origin of those arguments,
-you may want to be more circumspect. Enforcing type checking at the boundary
-between your code and the outside world may make it safer to use duck typing
-inside those boundaries. The type specifications also form a convenient
-remote API reference you can publish for prospective clients of your
-remotely-invokable service.</p>
-
-<p>In addition, these Constraints are enforced on each token as it arrives
-over the wire. This means that you can calculate a (small) upper bound on how
-much received data your program will store before it decides to hang up on
-the violator, minimizing your exposure to DoS attacks that involve sending
-random junk at you.</p>
-
-<p>There are three pieces you need to know about: Tokens, Constraints, and
-RemoteInterfaces.</p>
-
-<h3>Tokens</h3>
-
-<p>The fundamental unit of serialization is the Banana Token. These are
-thoroughly documented in the <a href="specifications/banana.xhtml">Banana
-Specification</a>, but what you need to know here is that each piece of
-non-container data, like a string or a number, is represented by a single
-token. Containers (like lists and dictionaries) are represented by a special
-OPEN token, followed by tokens for everything that is in the container,
-followed by the CLOSE token. Everything Banana does is in terms of these
-nested OPEN/stuff/stuff/CLOSE sequences of tokens.</p>
-
-<p>Each token consists of a header, a type byte, and an optional body. The
-header is always a base-128 number with a maximum of 64 digits, and the type
-byte is always a single byte. The body (if present) has a length dictated by
-the magnitude of the header.</p>
-
-<p>The length-first token format means that the receiving system never has to
-accept more than 65 bytes before it knows the type and size of the token, at
-which point it can make a decision about accepting or rejecting the rest of
-it.</p>
-
-<h3>Constraints</h3>
-
-<p>The schema <code>foolscap.schema</code> module has a variety of <code
-class="API" base="foolscap.schema">Constraint</code> classes that can be
-applied to incoming data. Most of them correspond to typical Python types,
-e.g. <code class="API" base="foolscap.schema">ListOf</code> matches a list,
-with a certain maximum length, and a child <code>Constraint</code> that gets
-applied to the contents of the list. You can nest <code>Constraint</code>s in
-this way to describe the <q>shape</q> of the object graph that you are
-willing to accept.</p>
-
-<p>At any given time, the receiving Banana protocol has single
-<code>Constraint</code> object that it enforces against the inbound data
-stream<span class="footnote">to be precise, each <code>Unslicer</code> on the
-receive stack has a <code>Constraint</code>, and the idea is that all of them
-get to pass judgement on the inbound token. A useful syntax to describe this
-sort of thing is still being worked out.</span>.</p>
-
-<h3>RemoteInterfaces</h3>
-
-<p>The <code class="API"
-base="foolscap.remoteinterface">RemoteInterface</code> is how you describe
-your constraints. You can provide a constraint for each argument of each
-method, as well as one for the return value. You can also specify addtional
-flags on the methods. The convention (which is actually enforced by the code)
-is to name <code>RemoteInterface</code> objects with an <q>RI</q> prefix,
-like <code>RIFoo</code>.</p>
-
-<p><code>RemoteInterfaces</code> are created and used a lot like the usual
-<code>zope.interface</code>-style <code>Interface</code>. They look like
-class definitions, inheriting from <code>RemoteInterface</code>. For each
-method, the default value of each argument is used to create a
-<code>Constraint</code> for that argument. Basic types (<code>int</code>,
-<code>str</code>, <code>bool</code>) are converted into a
-<code>Constraint</code> subclass (<code class="API"
-base="foolscap.schema">IntegerConstraint</code>, <code class="API"
-base="foolscap.schema">StringConstraint</code>, <code class="API"
-base="foolscap.schema">BooleanConstraint</code>). You can also use
-instances of other <code>Constraint</code> subclasses, like <code class="API"
-base="foolscap.schema">ListOf</code> and <code class="API"
-base="foolscap.schema">DictOf</code>. This <code>Constraint</code> will be
-enforced against the value for the given argument. Unless you specify
-otherwise, remote callers must match all the <code>Constraint</code>s you
-specify, all arguments listed in the RemoteInterface must be present, and no
-arguments outside that list will be accepted.</p>
-
-<p>Note that, like zope.interface, these methods should <b>not</b> include
-<q><code>self</code></q> in their argument list. This is because you are
-documenting how <em>other</em> people invoke your methods. <code>self</code>
-is an implementation detail. <code>RemoteInterface</code> will complain if
-you forget.</p>
-
-<p>The <q>methods</q> in a <code>RemoteInterface</code> should return a
-single value with the same format as the default arguments: either a basic
-type (<code>int</code>, <code>str</code>, etc) or a <code>Constraint</code>
-subclass. This <code>Constraint</code> is enforced on the return value of the
-method. If you are calling a method in somebody else's process, the argument
-constraints will be applied as a courtesy (<q>be conservative in what you
-send</q>), and the return value constraint will be applied to prevent the
-server from doing evil things to you. If you are running a method on behalf
-of a remote client, the argument constraints will be enforced to protect
-<em>you</em>, while the return value constraint will be applied as a
-courtesy.</p>
-
-<p>Attempting to send a value that does not satisfy the Constraint will
-result in a <code class="API" base="foolscap">Violation</code> exception
-being raised.</p>
-
-<p>You can also specify methods by defining attributes of the same name in
-the <code>RemoteInterface</code> object. Each attribute value should be an
-instance of <code class="API"
-base="foolscap.schema">RemoteMethodSchema</code><span
-class="footnote">although technically it can be any object which implements
-the <code class="API" base="foolscap.schema">IRemoteMethodConstraint</code>
-interface</span>. This approach is more flexible: there are some constraints
-that are not easy to express with the default-argument syntax, and this is
-the only way to set per-method flags. Note that all such method-defining
-attributes must be set in the <code>RemoteInterface</code> body itself,
-rather than being set on it after the fact (i.e. <code>RIFoo.doBar =
-stuff</code>). This is required because the <code>RemoteInterface</code>
-metaclass magic processes all of these attributes only once, immediately
-after the <code>RemoteInterface</code> body has been evaluated.</p>
-
-<p>The <code>RemoteInterface</code> <q>class</q> has a name. Normally this is
-the (short) classname<span
-class="footnote"><code>RIFoo.__class__.__name__</code>, if
-<code>RemoteInterface</code>s were actually classes, which they're
-not</span>. You can override this
-name by setting a special <code>__remote_name__</code> attribute on the
-<code>RemoteInterface</code> (again, in the body). This name is important
-because it is externally visible: all <code>RemoteReference</code>s that
-point at your <code>Referenceable</code>s will remember the name of the
-<code>RemoteInterface</code>s it implements. This is what enables the
-type-checking to be performed on both ends of the wire.</p>
-
-<p>In the future, this ought to default to the <b>fully-qualified</b>
-classname (like <code>package.module.RIFoo</code>), so that two
-RemoteInterfaces with the same name in different modules can co-exist. In the
-current release, these two RemoteInterfaces will collide (and provoke an
-import-time error message complaining about the duplicate name). As a result,
-if you have such classes (e.g. <code>foo.RIBar</code> and
-<code>baz.RIBar</code>), you <b>must</b> use <code>__remote_name__</code> to
-distinguish them (by naming one of them something other than
-<code>RIBar</code> to avoid this error.
-
-Hopefully this will be improved in a future version, but it looks like a
-difficult change to implement, so the standing recommendation is to use
-<code>__remote_name__</code> on all your RemoteInterfaces, and set it to a
-suitably unique string (like a URI).</p>
-
-<p>Here's an example:</p>
-
-<pre class="python">
-from foolscap import RemoteInterface, schema
-
-class RIMath(RemoteInterface):
- __remote_name__ = "RIMath.using-pb.docs.foolscap.twistedmatrix.com"
- def add(a=int, b=int):
- return int
- # declare it with an attribute instead of a function definition
- subtract = schema.RemoteMethodSchema(a=int, b=int, _response=int)
- def sum(args=schema.ListOf(int)):
- return int
-</pre>
-
-
-<h3>Using RemoteInterface</h3>
-
-<p>To declare that your <code>Referenceable</code> responds to a particular
-<code>RemoteInterface</code>, use the normal <code>implements()</code>
-annotation:</p>
-
-<pre class="python">
-class MathServer(foolscap.Referenceable):
- implements(RIMath)
-
- def remote_add(self, a, b):
- return a+b
- def remote_subtract(self, a, b):
- return a-b
- def remote_sum(self, args):
- total = 0
- for a in args: total += a
- return total
-</pre>
-
-<p>To enforce constraints everywhere, both sides will need to know about the
-<code>RemoteInterface</code>, and both must know it by the same name. It is a
-good idea to put the <code>RemoteInterface</code> in a common file that is
-imported into the programs running on both sides. It is up to you to make
-sure that both sides agree on the interface. Future versions of Foolscap may
-implement some sort of checksum-verification or Interface-serialization as a
-failsafe, but fundamentally the <code>RemoteInterface</code> that
-<em>you</em> are using defines what <em>your</em> program is prepared to
-handle. There is no difference between an old client accidentaly using a
-different version of the RemoteInterface by mistake, and a malicious attacker
-actively trying to confuse your code. The only promise that Foolscap can make
-is that the constraints you provide in the RemoteInterface will be faithfully
-applied to the incoming data stream, so that you don't need to do the type
-checking yourself inside the method.</p>
-
-<p>When making a remote method call, you use the <code>RemoteInterface</code>
-to identify the method instead of a string. This scopes the method name to
-the RemoteInterface:</p>
-
-<pre class="python">
-d = remote.callRemote(RIMath["add"], a=1, b=2)
-# or
-d = remote.callRemote(RIMath["add"], 1, 2)
-</pre>
-
-<h2>Pass-By-Copy</h2>
-
-<p>You can pass (nearly) arbitrary instances over the wire. Foolscap knows
-how to serialize all of Python's native data types already: numbers, strings,
-unicode strings, booleans, lists, tuples, dictionaries, sets, and the None
-object. You can teach it how to serialize instances of other types too.
-Foolscap will not serialize (or deserialize) any class that you haven't
-taught it about, both for security and because it refuses the temptation to
-guess your intentions about how these unknown classes ought to be
-serialized.</p>
-
-<p>The simplest possible way to pass things by copy is demonstrated in the
-following code fragment:</p>
-
-<pre class="python">
-from foolscap import Copyable, RemoteCopy
-
-class MyPassByCopy(Copyable, RemoteCopy):
- typeToCopy = copytype = "MyPassByCopy"
- def __init__(self):
- # RemoteCopy subclasses may not accept any __init__ arguments
- pass
- def setCopyableState(self, state):
- self.__dict__ = state
-</pre>
-
-<p>If the code on both sides of the wire import this class, then any
-instances of <code>MyPassByCopy</code> that are present in the arguments of a
-remote method call (or returned as the result of a remote method call) will
-be serialized and reconstituted into an equivalent instance on the other
-side.</p>
-
-<p>For more complicated things to do with pass-by-copy, see the documentation
-on <a href="copyable.html">Copyable</a>. This explains the difference between
-<code>Copyable</code> and <code>RemoteCopy</code>, how to control the
-serialization and deserialization process, and how to arrange for
-serialization of third-party classes that are not subclasses of
-<code>Copyable</code>.</p>
-
-
-<h2>Third-party References</h2><a name="introductions" />
-
-<p>Another new feature of Foolscap is the ability to send
-<code>RemoteReference</code>s to third parties. The classic scenario for this
-is illustrated by the <a
-href="http://www.erights.org/elib/capability/overview.html">three-party
-Granovetter diagram</a>. One party (Alice) has RemoteReferences to two other
-objects named Bob and Carol. She wants to share her reference to Carol with
-Bob, by including it in a message she sends to Bob (i.e. by using it as an
-argument when she invokes one of Bob's remote methods). The Foolscap code for
-doing this would look like:</p>
-
-<pre class="python">
-bobref.callRemote("foo", intro=carolref)
-</pre>
-
-<p>When Bob receives this message (i.e. when his <code>remote_foo</code>
-method is invoked), he will discover that he's holding a fully-functional
-<code>RemoteReference</code> to the object named Carol<span
-class="footnote">and if everyone involved is using authenticated Tubs, then
-Foolscap offers a guarantee, in the cryptographic sense, that Bob will wind
-up with a reference to the same object that Alice intended. The authenticated
-PBURLs prevent DNS-spoofing and man-in-the-middle attacks.</span>. He can
-start using this RemoteReference right away:</p>
-
-<pre class="python">
-class Bob(foolscap.Referenceable):
- def remote_foo(self, intro):
- self.carol = intro
- carol.callRemote("howdy", msg="Pleased to meet you", you=intro)
- return carol
-</pre>
-
-<p>If Bob sends this <code>RemoteReference</code> back to Alice, her method
-will see the same <code>RemoteReference</code> that she sent to Bob. In this
-example, Bob sends the reference by returning it from the original
-<code>remote_foo</code> method call, but he could almost as easily send it in
-a separate method call.</p>
-
-<pre class="python">
-class Alice(foolscap.Referenceable):
- def start(self, carol):
- self.carol = carol
- d = self.bob.callRemote("foo", intro=carol)
- d.addCallback(self.didFoo)
- def didFoo(self, result):
- assert result is self.carol # this will be true
-</pre>
-
-<p>Moreover, if Bob sends it back to <em>Carol</em> (completing the
-three-party round trip), Carol will see it as her original
-<code>Referenceable</code>.</p>
-
-<pre class="python">
-class Carol(foolscap.Referenceable):
- def remote_howdy(self, msg, you):
- assert you is self # this will be true
-</pre>
-
-<p>In addition to this, in the four-party introduction sequence as used by
-the <a
-href="http://www.erights.org/elib/equality/grant-matcher/index.html">Grant
-Matcher Puzzle</a>, when a Referenceable is sent to the same destination
-through multiple paths, the recipient will receive the same
-<code>RemoteReference</code> object from both sides.</p>
-
-<p>For a <code>RemoteReference</code> to be transferrable to third-parties in
-this fashion, the original <code>Referenceable</code> must live in a Tub
-which has a working listening port, and an established base URL. It is not
-necessary for the Referenceable to have been published with
-<code>registerReference</code> first: if it is sent over the wire before a
-name has been associated with it, it will be registered under a new random
-and unguessable name. The <code>RemoteReference</code> will contain the
-resulting URL, enabling it to be sent to third parties.</p>
-
-<p>When this introduction is made, the receiving system must establish a
-connection with the Tub that holds the original Referenceable, and acquire
-its own RemoteReference. These steps must take place before the remote method
-can be invoked, and other method calls might arrive before they do. All
-subsequent method calls are queued until the one that involved the
-introduction is performed. Foolscap guarantees (by default) that the messages
-sent to a given Referenceable will be delivered in the same order. In the
-future there may be options to relax this guarantee, in exchange for higher
-performance, reduced memory consumption, multiple priority queues, limited
-latency, or other features. There might even be an option to turn off
-introductions altogether.</p>
-
-<p>Also note that enabling this capability means any of your communication
-peers can make you create TCP connections to hosts and port numbers of their
-choosing. The fact that those connections can only speak the Foolscap
-protocol may reduce the security risk presented, but it still lets other
-people be annoying.</p>
-
-
-</body></html>
"""Foolscap"""
-__version__ = "0.1.3"
+__version__ = "0.1.4"
# here are the primary entry points
from foolscap.pb import Tub, UnauthenticatedTub, getRemoteURL_TCP
class ArgumentUnslicer(slicer.ScopedUnslicer):
methodSchema = None
+ debug = False
def setConstraint(self, methodSchema):
self.methodSchema = methodSchema
def start(self, count):
+ if self.debug:
+ log.msg("%s.start: %s" % (self, count))
self.numargs = None
self.args = []
self.kwargs = {}
return unslicer
def receiveChild(self, token, ready_deferred=None):
+ if self.debug:
+ log.msg("%s.receiveChild: %s %s %s %s %s args=%s kwargs=%s" %
+ (self, self.closed, self.num_unreferenceable_children,
+ self.num_unready_children, token, ready_deferred,
+ self.args, self.kwargs))
if self.numargs is None:
# this token is the number of positional arguments
assert isinstance(token, int)
argpos = len(self.args)
self.args.append(argvalue)
if isinstance(argvalue, defer.Deferred):
+ # this may occur if the child is a gift which has not
+ # resolved yet.
self.num_unreferenceable_children += 1
argvalue.addCallback(self.updateChild, argpos)
argvalue.addErrback(self.explode)
if ready_deferred:
+ if self.debug:
+ log.msg("%s.receiveChild got an unready posarg" % self)
self.num_unready_children += 1
ready_deferred.addCallback(self.childReady)
if len(self.args) < self.numargs:
argvalue.addCallback(self.updateChild, self.argname)
argvalue.addErrback(self.explode)
if ready_deferred:
+ if self.debug:
+ log.msg("%s.receiveChild got an unready kwarg" % self)
self.num_unready_children += 1
ready_deferred.addCallback(self.childReady)
self.argname = None
return
-
+
def updateChild(self, obj, which):
# one of our arguments has just now become referenceable. Normal
# types can't trigger this (since the arguments to a method form a
# RemoteReference, but for now all we get is a Deferred as a
# placeholder.
+ if self.debug:
+ log.msg("%s.updateChild, [%s] became referenceable: %s" %
+ (self, which, obj))
if isinstance(which, int):
self.args[which] = obj
else:
def childReady(self, obj):
self.num_unready_children -= 1
+ if self.debug:
+ log.msg("%s.childReady, now %d left" %
+ (self, self.num_unready_children))
+ log.msg(" obj=%s, args=%s, kwargs=%s" %
+ (obj, self.args, self.kwargs))
self.checkComplete()
return obj
def checkComplete(self):
# this is called each time one of our children gets updated or
# becomes ready (like when a Gift is finally resolved)
+ if self.debug:
+ log.msg("%s.checkComplete: %s %s %s args=%s kwargs=%s" %
+ (self, self.closed, self.num_unreferenceable_children,
+ self.num_unready_children, self.args, self.kwargs))
+
if not self.closed:
return
if self.num_unreferenceable_children:
if self.num_unready_children:
return
# yup, we're done. Notify anyone who is still waiting
+ if self.debug:
+ log.msg(" we are ready")
for d in self.watchers:
eventually(d.callback, self)
del self.watchers
def receiveClose(self):
+ if self.debug:
+ log.msg("%s.receiveClose: %s %s %s" %
+ (self, self.closed, self.num_unreferenceable_children,
+ self.num_unready_children))
if (self.numargs is None or
len(self.args) < self.numargs or
self.argname is not None):
raise BananaError("'arguments' sequence ended too early")
self.closed = True
self.watchers = []
+ # we don't return a ready_deferred. Instead, the InboundDelivery
+ # object queries our isReady() method directly.
return self, None
def isReady(self):
class CallUnslicer(slicer.ScopedUnslicer):
+ debug = False
+
def start(self, count):
# start=0:reqID, 1:objID, 2:methodname, 3: arguments
self.stage = 0
def receiveChild(self, token, ready_deferred=None):
assert not isinstance(token, defer.Deferred)
assert ready_deferred is None
- #print "CallUnslicer.receiveChild [s%d]" % self.stage, repr(token)
+ if self.debug:
+ log.msg("%s.receiveChild [s%d]: %s" %
+ (self, self.stage, repr(token)))
if self.stage == 0: # reqID
# we don't yet know which reqID to send any failure to
from twisted.application import service, strports
from twisted.python import log
-from foolscap import ipb, base32, negotiate, broker, observer
+from foolscap import ipb, base32, negotiate, broker, observer, eventual
from foolscap.referenceable import SturdyRef
from foolscap.tokens import PBError, BananaError
from foolscap.reconnector import Reconnector
self._activeConnectors = []
self._allConnectorsAreFinished = observer.OneShotObserverList()
+ self._pending_getReferences = [] # list of (d, furl) pairs
+
def setOption(self, name, value):
if name == "logLocalFailures":
# log (with log.err) any exceptions that occur during the
if not self.running and not self._activeConnectors:
self._allConnectorsAreFinished.fire(self)
+ def startService(self):
+ service.MultiService.startService(self)
+ for d,sturdy in self._pending_getReferences:
+ d1 = eventual.fireEventually(sturdy)
+ d1.addCallback(self.getReference)
+ d1.addBoth(lambda res, d=d: d.callback(res))
+ del self._pending_getReferences
+ for rc in self.reconnectors:
+ eventual.eventually(rc.startConnecting, self)
def _tubsAreNotRestartable(self):
raise RuntimeError("Sorry, but Tubs cannot be restarted.")
# note that once you stopService a Tub, I cannot be restarted. (at
# least this code is not designed to make that possible.. it might be
# doable in the future).
+ assert self.running
self.startService = self._tubsAreNotRestartable
self.getReference = self._tubHasBeenShutDown
self.connectTo = self._tubHasBeenShutDown
@return: a Deferred that fires with the RemoteReference
"""
- assert self.running
-
if isinstance(sturdyOrURL, SturdyRef):
sturdy = sturdyOrURL
else:
"we cannot handle encrypted PB-URLs like %s"
% sturdy.getURL())
return defer.fail(e)
+
+ if not self.running:
+ # queue their request for service once the Tub actually starts
+ log.msg("Tub.getReference(%s) queued until Tub.startService called"
+ % sturdy)
+ d = defer.Deferred()
+ self._pending_getReferences.append((d, sturdy))
+ return d
+
name = sturdy.name
d = self.getBrokerForTubRef(sturdy.getTubRef())
d.addCallback(lambda b: b.getYourReferenceByName(name))
return d
- def connectTo(self, sturdyOrURL, cb, *args, **kwargs):
+ def connectTo(self, _sturdyOrURL, _cb, *args, **kwargs):
"""Establish (and maintain) a connection to a given PBURL.
I establish a connection to the PBURL and run a callback to inform
rc.stopConnecting() # later
"""
- assert self.running
- rc = Reconnector(self, sturdyOrURL, cb, *args, **kwargs)
+ rc = Reconnector(_sturdyOrURL, _cb, args, kwargs)
+ if self.running:
+ rc.startConnecting(self)
+ else:
+ log.msg("Tub.connectTo(%s) queued until Tub.startService called"
+ % _sturdyOrURL)
self.reconnectors.append(rc)
return rc
jitter = 0.11962656492 # molar Planck constant times c, Joule meter/mole
verbose = False
- def __init__(self, tub, url, cb, *args, **kwargs):
- self._tub = tub
+ def __init__(self, url, cb, args, kwargs):
self._url = url
self._active = False
self._observer = (cb, args, kwargs)
self._delay = self.initialDelay
self._retries = 0
self._timer = None
- self.startConnecting()
+ self._tub = None
- def startConnecting(self):
+ def startConnecting(self, tub):
+ self._tub = tub
if self.verbose:
log.msg("Reconnector starting for %s" % self._url)
self._active = True
if self._timer:
self._timer.cancel()
self._timer = False
- self._tub._removeReconnector(self)
+ if self._tub:
+ self._tub._removeReconnector(self)
def _connect(self):
d = self._tub.getReference(self._url)
d.addBoth(self.ackGift)
# we return a Deferred that will fire with the RemoteReference when
# it becomes available. The RemoteReference is not even referenceable
- # until then.
- return d,None
+ # until then. In addition, we provide a ready_deferred, since any
+ # mutable container which holds the gift will be referenceable early
+ # but the message delivery must still wait for the getReference to
+ # complete. See to it that we fire the object deferred before we fire
+ # the ready_deferred.
+ obj_deferred, ready_deferred = defer.Deferred(), defer.Deferred()
+ def _ready(rref):
+ obj_deferred.callback(rref)
+ ready_deferred.callback(rref)
+ d.addCallback(_ready)
+
+ return obj_deferred, ready_deferred
def ackGift(self, rref):
rb = self.broker.remote_broker
cmp(self.__class__, them.__class__) or
cmp(self._distinguishers(), them._distinguishers()))
- def asLiveRef(self):
- """Return an object that can be sent over the wire and unserialized
- as a live RemoteReference on the far end. Use this when you have a
- SturdyRef and want to give someone a reference to its target, but
- when you haven't bothered to acquire your own live reference to it."""
-
- return _AsLiveRef(self)
-
-class _AsLiveRef:
- implements(ipb.ISlicer)
-
- def __init__(self, sturdy):
- self.target = sturdy
-
- def slice(self, streamable, banana):
- yield 'their-reference'
- yield giftID
- yield self.target.getURL()
- yield [] # interfacenames
-
class TubRef:
"""This is a little helper class which provides a comparable identifier
if isinstance(i, RemoteInterfaceClass):
if i not in ilist:
ilist.append(i)
- assert len(ilist) <= 1, "don't use multiple RemoteInterfaces! %s" % (obj,)
+ assert len(ilist) <= 1, ("don't use multiple RemoteInterfaces! %s uses %s"
+ % (obj, ilist))
if ilist:
return ilist[0]
return None
raise InvalidRemoteInterface(why)
if not names:
typeList = []
- if len(names) != len(typeList):
+ # 'def foo(oops)' results in typeList==None
+ if typeList is None or len(names) != len(typeList):
# TODO: relax this, use schema=Any for the args that don't have
# default values. This would make:
# def foo(a, b=int): return None
associated with a remote Referenceable that implements the given
RemoteInterface. If 'interface' is None, just assert that it is a
RemoteReference at all.
+
+ On the inbound side, this will only accept a suitably-implementing
+ RemoteReference, or a gift that resolves to such a RemoteReference. On
+ the outbound side, this will accept either a Referenceable or a
+ RemoteReference (which might be a your-reference or a their-reference).
+
+ Sending your-references will result in the recipient getting a local
+ Referenceable, which will not pass the constraint. TODO: think about if
+ we want this behavior or not.
"""
- opentypes = [("my-reference",)]
- # TODO: accept their-references too
+
+ opentypes = [("my-reference",), ("their-reference",)]
name = "RemoteInterfaceConstraint"
def __init__(self, interface):
% (obj, self.interface))
else:
# this ought to be a Referenceable which implements the desired
- # interface
+ # interface. Or, it might be a RemoteReference which points to
+ # one.
+ if ipb.IRemoteReference.providedBy(obj):
+ # it's a RemoteReference
+ if not self.interface:
+ return
+ iface = obj.tracker.interface
+ if not iface or iface != self.interface:
+ raise Violation("'%s' does not provide RemoteInterface %s"
+ % (obj, self.interface))
+ return
if not ipb.IReferenceable.providedBy(obj):
# TODO: maybe distinguish between OnlyReferenceable and
# Referenceable? which is more useful here?
from foolscap.slicers.list import ListSlicer, ListUnslicer
from foolscap.slicers.tuple import TupleSlicer, TupleUnslicer
from foolscap.slicers.set import SetSlicer, SetUnslicer
-from foolscap.slicers.set import ImmutableSetSlicer, ImmutableSetUnslicer
+from foolscap.slicers.set import FrozenSetSlicer, FrozenSetUnslicer
#from foolscap.slicers.set import BuiltinSetSlicer
from foolscap.slicers.dict import DictSlicer, DictUnslicer, OrderedDictSlicer
from foolscap.slicers.vocab import ReplaceVocabSlicer, ReplaceVocabUnslicer
ListSlicer, ListUnslicer,
TupleSlicer, TupleUnslicer,
SetSlicer, SetUnslicer,
- ImmutableSetSlicer, ImmutableSetUnslicer,
+ FrozenSetSlicer, FrozenSetUnslicer,
#from foolscap.slicers.set import BuiltinSetSlicer
DictSlicer, DictUnslicer, OrderedDictSlicer,
ReplaceVocabSlicer, ReplaceVocabUnslicer,
# -*- test-case-name: foolscap.test.test_banana -*-
from twisted.python import log
-from twisted.internet.defer import Deferred
+from twisted.internet.defer import Deferred, DeferredList
from foolscap.tokens import Violation, BananaError
from foolscap.slicer import BaseSlicer, BaseUnslicer
from foolscap.constraint import OpenerConstraint, Any, UnboundedSchema, IConstraint
self.d = {}
self.protocol.setObject(count, self.d)
self.key = None
+ self._ready_deferreds = []
def checkToken(self, typebyte, size):
if self.maxKeys != None:
self.d[key] = value
def receiveChild(self, obj, ready_deferred=None):
- assert not isinstance(obj, Deferred)
- assert ready_deferred is None
+ if ready_deferred:
+ self._ready_deferreds.append(ready_deferred)
if self.gettingKey:
self.receiveKey(obj)
else:
self.d[self.key] = value # placeholder
def receiveClose(self):
- return self.d, None
+ ready_deferred = None
+ if self._ready_deferreds:
+ ready_deferred = DeferredList(self._ready_deferreds)
+ return self.d, ready_deferred
def describe(self):
if self.gettingKey:
# -*- test-case-name: foolscap.test.test_banana -*-
from twisted.python import log
-from twisted.internet.defer import Deferred
+from twisted.internet.defer import Deferred, DeferredList
from foolscap.tokens import Violation
from foolscap.slicer import BaseSlicer, BaseUnslicer
from foolscap.constraint import OpenerConstraint, Any, UnboundedSchema, IConstraint
self.list = []
self.count = count
if self.debug:
- print "%s[%d].start with %s" % (self, self.count, self.list)
+ log.msg("%s[%d].start with %s" % (self, self.count, self.list))
self.protocol.setObject(count, self.list)
+ self._ready_deferreds = []
def checkToken(self, typebyte, size):
if self.maxLength != None and len(self.list) >= self.maxLength:
def update(self, obj, index):
# obj has already passed typechecking
if self.debug:
- print "%s[%d].update: [%d]=%s" % (self, self.count, index, obj)
+ log.msg("%s[%d].update: [%d]=%s" % (self, self.count, index, obj))
assert isinstance(index, int)
self.list[index] = obj
return obj
def receiveChild(self, obj, ready_deferred=None):
- assert ready_deferred is None
+ if ready_deferred:
+ self._ready_deferreds.append(ready_deferred)
if self.debug:
- print "%s[%d].receiveChild(%s)" % (self, self.count, obj)
+ log.msg("%s[%d].receiveChild(%s)" % (self, self.count, obj))
# obj could be a primitive type, a Deferred, or a complex type like
# those returned from an InstanceUnslicer. However, the individual
# object has already been through the schema validation process. The
raise Violation("the list is full")
if isinstance(obj, Deferred):
if self.debug:
- print " adding my update[%d] to %s" % (len(self.list), obj)
+ log.msg(" adding my update[%d] to %s" % (len(self.list), obj))
obj.addCallback(self.update, len(self.list))
obj.addErrback(self.printErr)
- self.list.append("placeholder")
+ placeholder = "list placeholder for arg[%d], rd=%s" % \
+ (len(self.list), ready_deferred)
+ self.list.append(placeholder)
else:
self.list.append(obj)
log.err(why)
def receiveClose(self):
- return self.list, None
+ ready_deferred = None
+ if self._ready_deferreds:
+ ready_deferred = DeferredList(self._ready_deferreds)
+ return self.list, ready_deferred
def describe(self):
return "[%d]" % len(self.list)
# -*- test-case-name: foolscap.test.test_banana -*-
import sets
-from foolscap.slicers.list import ListSlicer, ListUnslicer
+from twisted.internet import defer
+from twisted.python import log
+from foolscap.slicers.list import ListSlicer
+from foolscap.slicers.tuple import TupleUnslicer
+from foolscap.slicer import BaseUnslicer
from foolscap.tokens import Violation
from foolscap.constraint import OpenerConstraint, UnboundedSchema, Any, \
IConstraint
class SetSlicer(ListSlicer):
opentype = ("set",)
trackReferences = True
- slices = sets.Set
+ slices = set
def sliceBody(self, streamable, banana):
for i in self.obj:
yield i
-class ImmutableSetSlicer(SetSlicer):
+class FrozenSetSlicer(SetSlicer):
opentype = ("immutable-set",)
trackReferences = False
+ slices = frozenset
+
+# python2.4 has a builtin 'set' type, which is mutable, and we require
+# python2.4 or newer. Code which was written to be compatible with python2.3,
+# however, may use the 'sets' module. We will serialize old sets.Set and
+# sets.ImmutableSet the same as we serialize new set and frozenset.
+# Unfortunately this means that these objects will be deserialized as modern
+# 'set' and 'frozenset' objects, which are not entirely compatible. Therefore
+# code that is compatible with python2.3 might not work with foolscap.
+
+class OldSetSlicer(SetSlicer):
+ slices = sets.Set
+class OldImmutableSetSlicer(FrozenSetSlicer):
slices = sets.ImmutableSet
-have_builtin_set = False
-try:
- set
- # python2.4 has a builtin 'set' type, which is mutable
- have_builtin_set = True
- class BuiltinSetSlicer(SetSlicer):
- slices = set
- class BuiltinFrozenSetSlicer(ImmutableSetSlicer):
- slices = frozenset
-except NameError:
- # oh well, I guess we don't have 'set'
+class _Placeholder:
pass
-class SetUnslicer(ListUnslicer):
+class SetUnslicer(BaseUnslicer):
+ # this is a lot like a list, but sufficiently different to make it not
+ # worth subclassing
opentype = ("set",)
- def receiveClose(self):
- return sets.Set(self.list), None
+
+ debug = False
+ maxLength = None
+ itemConstraint = None
def setConstraint(self, constraint):
if isinstance(constraint, Any):
self.maxLength = constraint.maxLength
self.itemConstraint = constraint.constraint
-class ImmutableSetUnslicer(SetUnslicer):
+ def start(self, count):
+ #self.opener = foo # could replace it if we wanted to
+ self.set = set()
+ self.count = count
+ if self.debug:
+ log.msg("%s[%d].start with %s" % (self, self.count, self.set))
+ self.protocol.setObject(count, self.set)
+ self._ready_deferreds = []
+
+ def checkToken(self, typebyte, size):
+ if self.maxLength != None and len(self.set) >= self.maxLength:
+ # list is full, no more tokens accepted
+ # this is hit if the max+1 item is a primitive type
+ raise Violation("the set is full")
+ if self.itemConstraint:
+ self.itemConstraint.checkToken(typebyte, size)
+
+ def doOpen(self, opentype):
+ # decide whether the given object type is acceptable here. Raise a
+ # Violation exception if not, otherwise give it to our opener (which
+ # will normally be the RootUnslicer). Apply a constraint to the new
+ # unslicer.
+ if self.maxLength != None and len(self.set) >= self.maxLength:
+ # this is hit if the max+1 item is a non-primitive type
+ raise Violation("the set is full")
+ if self.itemConstraint:
+ self.itemConstraint.checkOpentype(opentype)
+ unslicer = self.open(opentype)
+ if unslicer:
+ if self.itemConstraint:
+ unslicer.setConstraint(self.itemConstraint)
+ return unslicer
+
+ def update(self, obj, placeholder):
+ # obj has already passed typechecking
+ if self.debug:
+ log.msg("%s[%d].update: [%s]=%s" % (self, self.count,
+ placeholder, obj))
+ self.set.remove(placeholder)
+ self.set.add(obj)
+ return obj
+
+ def receiveChild(self, obj, ready_deferred=None):
+ if ready_deferred:
+ self._ready_deferreds.append(ready_deferred)
+ if self.debug:
+ log.msg("%s[%d].receiveChild(%s)" % (self, self.count, obj))
+ # obj could be a primitive type, a Deferred, or a complex type like
+ # those returned from an InstanceUnslicer. However, the individual
+ # object has already been through the schema validation process. The
+ # only remaining question is whether the larger schema will accept
+ # it.
+ if self.maxLength != None and len(self.set) >= self.maxLength:
+ # this is redundant
+ # (if it were a non-primitive one, it would be caught in doOpen)
+ # (if it were a primitive one, it would be caught in checkToken)
+ raise Violation("the set is full")
+ if isinstance(obj, defer.Deferred):
+ if self.debug:
+ log.msg(" adding my update[%d] to %s" % (len(self.set), obj))
+ # note: the placeholder isn't strictly necessary, but it will
+ # help debugging to see a _Placeholder sitting in the set when it
+ # shouldn't rather than seeing a set that is smaller than it
+ # ought to be. If a remote method ever sees a _Placeholder, then
+ # something inside Foolscap has broken.
+ placeholder = _Placeholder()
+ obj.addCallback(self.update, placeholder)
+ obj.addErrback(self.printErr)
+ self.set.add(placeholder)
+ else:
+ self.set.add(obj)
+
+ def printErr(self, why):
+ print "ERR!"
+ print why.getBriefTraceback()
+ log.err(why)
+
+ def receiveClose(self):
+ ready_deferred = None
+ if self._ready_deferreds:
+ ready_deferred = defer.DeferredList(self._ready_deferreds)
+ return self.set, ready_deferred
+
+class FrozenSetUnslicer(TupleUnslicer):
opentype = ("immutable-set",)
+
def receiveClose(self):
- return sets.ImmutableSet(self.list), None
+ obj_or_deferred, ready_deferred = TupleUnslicer.receiveClose(self)
+ if isinstance(obj_or_deferred, defer.Deferred):
+ def _convert(the_tuple):
+ return frozenset(the_tuple)
+ obj_or_deferred.addCallback(_convert)
+ else:
+ obj_or_deferred = frozenset(obj_or_deferred)
+ return obj_or_deferred, ready_deferred
class SetConstraint(OpenerConstraint):
opentypes = [("set",), ("immutable-set",)]
name = "SetConstraint"
- if have_builtin_set:
- mutable_set_types = (set, sets.Set)
- immutable_set_types = (frozenset, sets.ImmutableSet)
- else:
- mutable_set_types = (sets.Set,)
- immutable_set_types = (sets.ImmutableSet,)
+ mutable_set_types = (set, sets.Set)
+ immutable_set_types = (frozenset, sets.ImmutableSet)
all_set_types = mutable_set_types + immutable_set_types
def __init__(self, constraint, maxLength=30, mutable=None):
# -*- test-case-name: foolscap.test.test_banana -*-
-from twisted.internet.defer import Deferred
+from twisted.internet.defer import Deferred, DeferredList
from foolscap.tokens import Violation
from foolscap.slicer import BaseUnslicer
from foolscap.slicers.list import ListSlicer
self.finished = False
self.deferred = Deferred()
self.protocol.setObject(count, self.deferred)
+ self._ready_deferreds = []
def checkToken(self, typebyte, size):
if self.constraints == None:
return obj
def receiveChild(self, obj, ready_deferred=None):
- assert ready_deferred is None
+ if ready_deferred:
+ self._ready_deferreds.append(ready_deferred)
if isinstance(obj, Deferred):
obj.addCallback(self.update, len(self.list))
obj.addErrback(self.explode)
# not finished yet, we'll fire our Deferred when we are
if self.debug:
print " not finished yet"
- return self.deferred, None
+ return
+
# list is now complete. We can finish.
+ return self.complete()
+
+ def complete(self):
+ ready_deferred = None
+ if self._ready_deferreds:
+ ready_deferred = DeferredList(self._ready_deferreds)
+
t = tuple(self.list)
if self.debug:
print " finished! tuple:%s{%s}" % (t, id(t))
self.protocol.setObject(self.count, t)
self.deferred.callback(t)
- return t, None
+ return t, ready_deferred
def receiveClose(self):
if self.debug:
print "%s[%d].receiveClose" % (self, self.count)
self.finished = 1
- return self.checkComplete()
+
+ if self.num_unreferenceable_children:
+ # not finished yet, we'll fire our Deferred when we are
+ if self.debug:
+ print " not finished yet"
+ ready_deferred = None
+ if self._ready_deferreds:
+ ready_deferred = DeferredList(self._ready_deferreds)
+ return self.deferred, ready_deferred
+
+ # the list is already complete
+ return self.complete()
def describe(self):
return "[%d]" % len(self.list)
def test_tuple(self):
return self.looptest((1,2))
def test_set(self):
- d = self.looptest(sets.Set([1,2]))
- d.addCallback(lambda res: self.looptest(sets.ImmutableSet([1,2])))
- # verify the python2.4 builtin 'set' type, which is mutable
- try:
- set
- have_set = True
- except NameError:
- have_set = False
- if have_set:
- # we serialize builtin 'set' as a regular mutable sets.Set
- d.addCallback(lambda res:
- self.looptest(set([1,2]), sets.Set([1,2])))
+ d = self.looptest(set([1,2]))
+ d.addCallback(lambda res: self.looptest(frozenset([1,2])))
+ # and verify that old sets turn into modern ones, which is
+ # unfortunate but at least consistent
+ d.addCallback(lambda res: self.looptest(sets.Set([1,2]), set([1,2])))
+ d.addCallback(lambda res: self.looptest(sets.ImmutableSet([1,2]),
+ frozenset([1,2])))
return d
def test_bool(self):
self.assertIdentical(z[0]['list'], z[1])
self.assertIdentical(z[0]['list'][0], z)
- def testMoreReferences(self):
+ def test_cycles_1(self):
+ # a list that contains a tuple that can't be referenced yet
a = []
- t = (a,)
- t2 = (t,)
+ t1 = (a,)
+ t2 = (t1,)
a.append(t2)
- d = self.loop(t)
+ d = self.loop(t1)
d.addCallback(lambda z: self.assertIdentical(z[0][0][0], z))
return d
+ def test_cycles_2(self):
+ # a dict that contains a tuple that can't be referenced yet.
+ a = {}
+ t1 = (a,)
+ t2 = (t1,)
+ a['foo'] = t2
+ d = self.loop(t1)
+ d.addCallback(lambda z: self.assertIdentical(z[0]['foo'][0], z))
+ return d
+
+ def test_cycles_3(self):
+ # sets seem to be transitively immutable: any mutable contents would
+ # be unhashable, and sets can only contain hashable objects.
+ # Therefore sets cannot participate in cycles the way that tuples
+ # can.
+
+ # a set that contains a tuple that can't be referenced yet. You can't
+ # actually create this in python, because you can only create a set
+ # out of hashable objects, and sets aren't hashable, and a tuple that
+ # contains a set is not hashable.
+ a = set()
+ t1 = (a,)
+ t2 = (t1,)
+ a.add(t2)
+ d = self.loop(t1)
+ d.addCallback(lambda z: self.assertIdentical(list(z[0])[0][0], z))
+
+ # a list that contains a frozenset that can't be referenced yet
+ a = []
+ t1 = frozenset([a])
+ t2 = frozenset([t1])
+ a.append(t2)
+ d = self.loop(t1)
+ d.addCallback(lambda z:
+ self.assertIdentical(list(list(z)[0][0])[0], z))
+
+ # a dict that contains a frozenset that can't be referenced yet.
+ a = {}
+ t1 = frozenset([a])
+ t2 = frozenset([t1])
+ a['foo'] = t2
+ d = self.loop(t1)
+ d.addCallback(lambda z:
+ self.assertIdentical(list(list(z)[0]['foo'])[0], z))
+
+ # a set that contains a frozenset that can't be referenced yet.
+ a = set()
+ t1 = frozenset([a])
+ t2 = frozenset([t1])
+ a.add(t2)
+ d = self.loop(t1)
+ d.addCallback(lambda z: self.assertIdentical(list(list(list(z)[0])[0])[0], z))
+ return d
+ del test_cycles_3
+
class VocabTest1(unittest.TestCase):
log.startLogging(sys.stderr)
from twisted.python import failure, log
-from twisted.internet import reactor, defer
from twisted.trial import unittest
from twisted.internet.main import CONNECTION_LOST
d.addCallback(lambda res: self.failUnlessEqual(target.calls, [(1,2)]))
d.addCallback(self._testCall1_1, rr)
return d
- testCall1.timeout = 3
def _testCall1_1(self, res, rr):
# the caller still holds the RemoteReference
self.failUnless(self.callingBroker.yourReferenceByCLID.has_key(1))
# the targetBroker so *they* can forget about it.
del rr # this fires a DecRef
gc.collect() # make sure
+
# we need to give it a moment to deliver the DecRef message and act
- # on it
- d = defer.Deferred()
- reactor.callLater(0.1, d.callback, None)
+ # on it. Poll until the caller has received it.
+ def _check():
+ if self.callingBroker.yourReferenceByCLID.has_key(1):
+ return False
+ return True
+ d = self.poll(_check)
d.addCallback(self._testCall1_2)
return d
def _testCall1_2(self, res):
+from zope.interface import implements
from twisted.trial import unittest
from twisted.internet import defer
from twisted.internet.error import ConnectionDone, ConnectionLost
-from foolscap import Tub, UnauthenticatedTub
+from foolscap import Tub, UnauthenticatedTub, RemoteInterface, Referenceable
from foolscap.referenceable import RemoteReference
-from foolscap.test.common import HelperTarget
+from foolscap.test.common import HelperTarget, RIHelper
from foolscap.eventual import flushEventualQueue
crypto_available = False
f.trap(ConnectionDone, ConnectionLost)
return None
+class RIConstrainedHelper(RemoteInterface):
+ def set(obj=RIHelper): return None
+
+
+class ConstrainedHelper(Referenceable):
+ implements(RIConstrainedHelper)
+
+ def __init__(self, name="unnamed"):
+ self.name = name
+
+ def remote_set(self, obj):
+ self.obj = obj
+
class Gifts(unittest.TestCase):
# Here we test the three-party introduction process as depicted in the
# classic Granovetter diagram. Alice has a reference to Bob and another
self.bob_url = self.tubB.registerReference(self.bob)
self.carol = HelperTarget("carol")
self.carol_url = self.tubC.registerReference(self.carol)
- self.cindy = HelperTarget("cindy")
# cindy is Carol's little sister. She doesn't have a phone, but
# Carol might talk about her anyway.
+ self.cindy = HelperTarget("cindy")
+ # more sisters. Alice knows them, and she introduces Bob to them.
+ self.charlene = HelperTarget("charlene")
+ self.christine = HelperTarget("christine")
+ self.clarisse = HelperTarget("clarisse")
+ self.colette = HelperTarget("colette")
+ self.courtney = HelperTarget("courtney")
def createInitialReferences(self):
# we must start by giving Alice a reference to both Bob and Carol.
d.addCallback(_aliceGotCarol)
return d
+ def createMoreReferences(self):
+ # give Alice references to Carol's sisters
+ dl = []
+
+ url = self.tubC.registerReference(self.charlene)
+ d = self.tubA.getReference(url)
+ def _got_charlene(rref):
+ self.acharlene = rref
+ d.addCallback(_got_charlene)
+ dl.append(d)
+
+ url = self.tubC.registerReference(self.christine)
+ d = self.tubA.getReference(url)
+ def _got_christine(rref):
+ self.achristine = rref
+ d.addCallback(_got_christine)
+ dl.append(d)
+
+ url = self.tubC.registerReference(self.clarisse)
+ d = self.tubA.getReference(url)
+ def _got_clarisse(rref):
+ self.aclarisse = rref
+ d.addCallback(_got_clarisse)
+ dl.append(d)
+
+ url = self.tubC.registerReference(self.colette)
+ d = self.tubA.getReference(url)
+ def _got_colette(rref):
+ self.acolette = rref
+ d.addCallback(_got_colette)
+ dl.append(d)
+
+ url = self.tubC.registerReference(self.courtney)
+ d = self.tubA.getReference(url)
+ def _got_courtney(rref):
+ self.acourtney = rref
+ d.addCallback(_got_courtney)
+ dl.append(d)
+ return defer.DeferredList(dl)
+
def testGift(self):
#defer.setDebugging(True)
self.createCharacters()
d.addCallback(_checkBob)
return d
+ def testContainers(self):
+ self.createCharacters()
+ self.bob.calls = []
+ d = self.createInitialReferences()
+ d.addCallback(lambda res: self.createMoreReferences())
+ def _introduce(res):
+ # we send several messages to Bob, each of which has a container
+ # with a gift inside it. This exercises the ready_deferred
+ # handling inside containers.
+ dl = []
+ cr = self.abob.callRemote
+ dl.append(cr("append", set([self.acharlene])))
+ dl.append(cr("append", frozenset([self.achristine])))
+ dl.append(cr("append", [self.aclarisse]))
+ dl.append(cr("append", obj=(self.acolette,)))
+ dl.append(cr("append", {'a': self.acourtney}))
+ # TODO: pass a gift as an attribute of a Copyable
+ return defer.DeferredList(dl)
+ d.addCallback(_introduce)
+ def _checkBob(res):
+ # this runs after all three messages have been acked by Bob
+ self.failUnlessEqual(len(self.bob.calls), 5)
+
+ bcharlene = self.bob.calls.pop(0)
+ self.failUnless(isinstance(bcharlene, set))
+ self.failUnlessEqual(len(bcharlene), 1)
+ self.failUnless(isinstance(list(bcharlene)[0], RemoteReference))
+
+ bchristine = self.bob.calls.pop(0)
+ self.failUnless(isinstance(bchristine, frozenset))
+ self.failUnlessEqual(len(bchristine), 1)
+ self.failUnless(isinstance(list(bchristine)[0], RemoteReference))
+
+ bclarisse = self.bob.calls.pop(0)
+ self.failUnless(isinstance(bclarisse, list))
+ self.failUnlessEqual(len(bclarisse), 1)
+ self.failUnless(isinstance(bclarisse[0], RemoteReference))
+
+ bcolette = self.bob.calls.pop(0)
+ self.failUnless(isinstance(bcolette, tuple))
+ self.failUnlessEqual(len(bcolette), 1)
+ self.failUnless(isinstance(bcolette[0], RemoteReference))
+
+ bcourtney = self.bob.calls.pop(0)
+ self.failUnless(isinstance(bcourtney, dict))
+ self.failUnlessEqual(len(bcourtney), 1)
+ self.failUnless(isinstance(bcourtney['a'], RemoteReference))
+
+ d.addCallback(_checkBob)
+ return d
+
+ def create_constrained_characters(self):
+ self.alice = HelperTarget("alice")
+ self.bob = ConstrainedHelper("bob")
+ self.bob_url = self.tubB.registerReference(self.bob)
+ self.carol = HelperTarget("carol")
+ self.carol_url = self.tubC.registerReference(self.carol)
+
+ def test_constraint(self):
+ self.create_constrained_characters()
+ self.bob.calls = []
+ d = self.createInitialReferences()
+ def _introduce(res):
+ return self.abob.callRemote("set", self.acarol)
+ d.addCallback(_introduce)
+ def _checkBob(res):
+ self.failUnless(isinstance(self.bob.obj, RemoteReference))
+ d.addCallback(_checkBob)
+ return d
+
# this was used to alice's reference to carol (self.acarol) appeared in
# alice's gift table at the right time, to make sure that the
# RemoteReference is kept alive while the gift is in transit. The whole
import sets, re
from twisted.trial import unittest
from foolscap import schema, copyable
-from foolscap.tokens import Violation
+from foolscap.tokens import Violation, InvalidRemoteInterface
from foolscap.constraint import IConstraint
from foolscap.remoteinterface import RemoteMethodSchema, \
RemoteInterfaceConstraint, LocalInterfaceConstraint
self.failUnlessRaises(schema.Violation,
r.checkResults, 12, False)
+ def test_bad_arguments(self):
+ def foo(nodefault): return str
+ self.failUnlessRaises(InvalidRemoteInterface,
+ RemoteMethodSchema, method=foo)
+ def bar(nodefault, a=int): return str
+ self.failUnlessRaises(InvalidRemoteInterface,
+ RemoteMethodSchema, method=bar)
class Interfaces(unittest.TestCase):
interfaceName = common.RIHelper.__remote_name__
tracker = RemoteReferenceTracker(parent, clid, url, interfaceName)
rr = RemoteReference(tracker)
+
c1 = RemoteInterfaceConstraint(common.RIHelper)
- c2 = RemoteInterfaceConstraint(common.RIMyTarget)
self.check_inbound(rr, c1)
- self.violates_outbound(rr, c1)
+ self.check_outbound(rr, c1) # gift
+
+ c2 = RemoteInterfaceConstraint(common.RIMyTarget)
self.violates_inbound(rr, c2)
self.violates_outbound(rr, c2)
import os.path
from twisted.trial import unittest
+from twisted.internet import defer
crypto_available = False
try:
except ImportError:
pass
-from foolscap import Tub
+from foolscap import Tub, UnauthenticatedTub
+from foolscap.referenceable import RemoteReference
+from foolscap.eventual import eventually, flushEventualQueue
+from foolscap.test.common import HelperTarget, TargetMixin
+
+# we use authenticated tubs if possible. If crypto is not available, fall
+# back to unauthenticated ones
+GoodEnoughTub = UnauthenticatedTub
+if crypto_available:
+ GoodEnoughTub = Tub
class TestCertFile(unittest.TestCase):
def test_generate(self):
if not crypto_available:
del TestCertFile
+
+class QueuedStartup(TargetMixin, unittest.TestCase):
+ # calling getReference and connectTo before the Tub has started should
+ # put off network activity until the Tub is started.
+
+ def setUp(self):
+ TargetMixin.setUp(self)
+ self.tubB = GoodEnoughTub()
+ self.services = [self.tubB]
+ for s in self.services:
+ s.startService()
+ l = s.listenOn("tcp:0:interface=127.0.0.1")
+ s.setLocation("127.0.0.1:%d" % l.getPortnum())
+
+ self.barry = HelperTarget("barry")
+ self.barry_url = self.tubB.registerReference(self.barry)
+
+ self.bill = HelperTarget("bill")
+ self.bill_url = self.tubB.registerReference(self.bill)
+
+ self.bob = HelperTarget("bob")
+ self.bob_url = self.tubB.registerReference(self.bob)
+
+ def tearDown(self):
+ d = TargetMixin.tearDown(self)
+ def _more(res):
+ return defer.DeferredList([s.stopService() for s in self.services])
+ d.addCallback(_more)
+ d.addCallback(flushEventualQueue)
+ return d
+
+ def test_queued_getref(self):
+ t1 = GoodEnoughTub()
+ d1 = t1.getReference(self.barry_url)
+ d2 = t1.getReference(self.bill_url)
+ def _check(res):
+ ((barry_success, barry_rref),
+ (bill_success, bill_rref)) = res
+ self.failUnless(barry_success)
+ self.failUnless(bill_success)
+ self.failUnless(isinstance(barry_rref, RemoteReference))
+ self.failUnless(isinstance(bill_rref, RemoteReference))
+ self.failIf(barry_rref == bill_success)
+ dl = defer.DeferredList([d1, d2])
+ dl.addCallback(_check)
+ self.services.append(t1)
+ eventually(t1.startService)
+ return dl
+
+ def test_queued_reconnector(self):
+ t1 = GoodEnoughTub()
+ bill_connections = []
+ barry_connections = []
+ t1.connectTo(self.bill_url, bill_connections.append)
+ t1.connectTo(self.barry_url, barry_connections.append)
+ def _check():
+ if len(bill_connections) >= 1 and len(barry_connections) >= 1:
+ return True
+ return False
+ d = self.poll(_check)
+ def _validate(res):
+ self.failUnless(isinstance(bill_connections[0], RemoteReference))
+ self.failUnless(isinstance(barry_connections[0], RemoteReference))
+ self.failIf(bill_connections[0] == barry_connections[0])
+ d.addCallback(_validate)
+ self.services.append(t1)
+ eventually(t1.startService)
+ return d
+
-foolscap (0.1.3) unstable; urgency=low
+foolscap (0.1.4) unstable; urgency=low
* new release
- -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
+ -- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
-foolscap (0.1.2+) unstable; urgency=low
+foolscap (0.1.3) unstable; urgency=low
- * bump revision while between releases
+ * new release
- -- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
+ -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
foolscap (0.1.2) unstable; urgency=low
-- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 12:32:46 -0700
-foolscap (0.1.1+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 10:32:22 -0700
-
foolscap (0.1.1) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 3 Apr 2007 20:48:07 -0700
-foolscap (0.1.0+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 19 Mar 2007 23:11:35 -0700
-
foolscap (0.1.0) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 15 Mar 2007 16:56:16 -0700
-foolscap (0.0.7+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 22 Jan 2007 12:41:18 -0800
-
foolscap (0.0.7) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 16 Jan 2007 12:03:00 -0800
-foolscap (0.0.6+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Thu, 4 Jan 2007 18:45:04 -0500
-
foolscap (0.0.6) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Mon, 18 Dec 2006 12:10:51 -0800
-foolscap (0.0.5+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 14 Nov 2006 21:24:17 -0800
-
foolscap (0.0.5) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Sat, 4 Nov 2006 23:20:46 -0800
-foolscap (0.0.4+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 31 Oct 2006 23:38:34 -0800
-
foolscap (0.0.4) unstable; urgency=low
* new release
- -- Brian Warner <warner@lothar.com> Thu, 26 Oct 2006 00:46:11 -0700
+ -- Brian Warner <warner@lothar.com> Thu, 26 Oct 2006 00:46:30 -0700
-foolscap (0.0.3+) unstable; urgency=low
+foolscap (0.0.3) unstable; urgency=low
* new upstream release, put debian packaging in the tree
dh_testdir
dh_testroot
dh_installdocs -i -A NEWS README
- dh_installdocs ChangeLog doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-pb.xhtml doc/copyable.xhtml doc/listings doc/specifications
+ dh_installdocs ChangeLog doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-foolscap.xhtml doc/copyable.xhtml doc/listings doc/specifications
dh_installchangelogs -i
dh_compress -i -X.py
dh_fixperms
-foolscap (0.1.3) unstable; urgency=low
+foolscap (0.1.4) unstable; urgency=low
* new release
- -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
+ -- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
-foolscap (0.1.2+) unstable; urgency=low
+foolscap (0.1.3) unstable; urgency=low
- * bump revision while between releases
+ * new release
- -- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
+ -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
foolscap (0.1.2) unstable; urgency=low
-- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 12:32:46 -0700
-foolscap (0.1.1+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 10:32:22 -0700
-
foolscap (0.1.1) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 3 Apr 2007 20:48:07 -0700
-foolscap (0.1.0+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 19 Mar 2007 23:11:35 -0700
-
foolscap (0.1.0) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 15 Mar 2007 16:56:16 -0700
-foolscap (0.0.7+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 22 Jan 2007 12:41:18 -0800
-
foolscap (0.0.7) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 16 Jan 2007 12:03:00 -0800
-foolscap (0.0.6+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Thu, 4 Jan 2007 18:45:04 -0500
-
foolscap (0.0.6) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Mon, 18 Dec 2006 12:10:51 -0800
-foolscap (0.0.5+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 14 Nov 2006 21:24:17 -0800
-
foolscap (0.0.5) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Sat, 4 Nov 2006 23:20:46 -0800
-foolscap (0.0.4+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 31 Oct 2006 23:38:34 -0800
-
foolscap (0.0.4) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 26 Oct 2006 00:46:30 -0700
-foolscap (0.0.3+) unstable; urgency=low
+foolscap (0.0.3) unstable; urgency=low
* new upstream release, put debian packaging in the tree
install/python-foolscap::
- dh_installdocs doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-pb.xhtml doc/copyable.xhtml doc/listings doc/specifications
+ dh_installdocs doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-foolscap.xhtml doc/copyable.xhtml doc/listings doc/specifications
clean::
-rm -rf build
-foolscap (0.1.3) unstable; urgency=low
+foolscap (0.1.4) unstable; urgency=low
* new release
- -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
+ -- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
-foolscap (0.1.2+) unstable; urgency=low
+foolscap (0.1.3) unstable; urgency=low
- * bump revision while between releases
+ * new release
- -- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
+ -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
foolscap (0.1.2) unstable; urgency=low
-- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 12:32:46 -0700
-foolscap (0.1.1+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 10:32:22 -0700
-
foolscap (0.1.1) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 3 Apr 2007 20:48:07 -0700
-foolscap (0.1.0+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 19 Mar 2007 23:11:35 -0700
-
foolscap (0.1.0) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 15 Mar 2007 16:56:16 -0700
-foolscap (0.0.7+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 22 Jan 2007 12:41:18 -0800
-
foolscap (0.0.7) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 16 Jan 2007 12:03:00 -0800
-foolscap (0.0.6+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Thu, 4 Jan 2007 18:45:04 -0500
-
foolscap (0.0.6) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Mon, 18 Dec 2006 12:10:51 -0800
-foolscap (0.0.5+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 14 Nov 2006 21:24:17 -0800
-
foolscap (0.0.5) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Sat, 4 Nov 2006 23:20:46 -0800
-foolscap (0.0.4+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 31 Oct 2006 23:38:34 -0800
-
foolscap (0.0.4) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 26 Oct 2006 00:46:30 -0700
-foolscap (0.0.3+) unstable; urgency=low
+foolscap (0.0.3) unstable; urgency=low
* new upstream release, put debian packaging in the tree
install/python-foolscap::
- dh_installdocs doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-pb.xhtml doc/copyable.xhtml doc/listings doc/specifications
+ dh_installdocs doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-foolscap.xhtml doc/copyable.xhtml doc/listings doc/specifications
clean::
-rm -rf build
-foolscap (0.1.3) unstable; urgency=low
+foolscap (0.1.4) unstable; urgency=low
* new release
- -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
+ -- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
-foolscap (0.1.2+) unstable; urgency=low
+foolscap (0.1.3) unstable; urgency=low
- * bump revision while between releases
+ * new release
- -- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
+ -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
foolscap (0.1.2) unstable; urgency=low
-- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 12:32:46 -0700
-foolscap (0.1.1+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 10:32:22 -0700
-
foolscap (0.1.1) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 3 Apr 2007 20:48:07 -0700
-foolscap (0.1.0+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 19 Mar 2007 23:11:35 -0700
-
foolscap (0.1.0) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 15 Mar 2007 16:56:16 -0700
-foolscap (0.0.7+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 22 Jan 2007 12:41:18 -0800
-
foolscap (0.0.7) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 16 Jan 2007 12:03:00 -0800
-foolscap (0.0.6+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Thu, 4 Jan 2007 18:45:04 -0500
-
foolscap (0.0.6) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Mon, 18 Dec 2006 12:10:51 -0800
-foolscap (0.0.5+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 14 Nov 2006 21:24:17 -0800
-
foolscap (0.0.5) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Sat, 4 Nov 2006 23:20:46 -0800
-foolscap (0.0.4+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 31 Oct 2006 23:38:34 -0800
-
foolscap (0.0.4) unstable; urgency=low
* new release
- -- Brian Warner <warner@lothar.com> Thu, 26 Oct 2006 00:46:11 -0700
+ -- Brian Warner <warner@lothar.com> Thu, 26 Oct 2006 00:46:30 -0700
-foolscap (0.0.3+) unstable; urgency=low
+foolscap (0.0.3) unstable; urgency=low
* new upstream release, put debian packaging in the tree
dh_testdir
dh_testroot
dh_installdocs -i -A NEWS README
- dh_installdocs ChangeLog doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-pb.xhtml doc/copyable.xhtml doc/listings doc/specifications
+ dh_installdocs ChangeLog doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-foolscap.xhtml doc/copyable.xhtml doc/listings doc/specifications
dh_installchangelogs -i
dh_compress -i -X.py
dh_fixperms
-foolscap (0.1.3) unstable; urgency=low
+foolscap (0.1.4) unstable; urgency=low
* new release
- -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
+ -- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
-foolscap (0.1.2+) unstable; urgency=low
+foolscap (0.1.3) unstable; urgency=low
- * bump revision while between releases
+ * new release
- -- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
+ -- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
foolscap (0.1.2) unstable; urgency=low
-- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 12:32:46 -0700
-foolscap (0.1.1+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Wed, 4 Apr 2007 10:32:22 -0700
-
foolscap (0.1.1) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 3 Apr 2007 20:48:07 -0700
-foolscap (0.1.0+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 19 Mar 2007 23:11:35 -0700
-
foolscap (0.1.0) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 15 Mar 2007 16:56:16 -0700
-foolscap (0.0.7+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Mon, 22 Jan 2007 12:41:18 -0800
-
foolscap (0.0.7) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Tue, 16 Jan 2007 12:03:00 -0800
-foolscap (0.0.6+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Thu, 4 Jan 2007 18:45:04 -0500
-
foolscap (0.0.6) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Mon, 18 Dec 2006 12:10:51 -0800
-foolscap (0.0.5+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 14 Nov 2006 21:24:17 -0800
-
foolscap (0.0.5) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Sat, 4 Nov 2006 23:20:46 -0800
-foolscap (0.0.4+) unstable; urgency=low
-
- * bump revision while between releases
-
- -- Brian Warner <warner@lothar.com> Tue, 31 Oct 2006 23:38:34 -0800
-
foolscap (0.0.4) unstable; urgency=low
* new release
-- Brian Warner <warner@lothar.com> Thu, 26 Oct 2006 00:46:30 -0700
-foolscap (0.0.3+) unstable; urgency=low
+foolscap (0.0.3) unstable; urgency=low
* new upstream release, put debian packaging in the tree
install/python-foolscap::
- dh_installdocs doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-pb.xhtml doc/copyable.xhtml doc/listings doc/specifications
+ dh_installdocs doc/newpb-jobs.txt doc/newpb-todo.txt doc/use-cases.txt doc/using-foolscap.xhtml doc/copyable.xhtml doc/listings doc/specifications
clean::
-rm -rf build
Start recording.
"""
if not self.started:
- self.LOG = open("/tmp/flog.out", "w")
self.started = True
sys.settrace(self.g)