From 267a068fe4d45bffe5f38e93498b80066e837e8c Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@allmydata.com>
Date: Tue, 15 May 2007 17:33:52 -0700
Subject: [PATCH] update to foolscap-0.1.4

---
 src/foolscap/ChangeLog                        | 109 +++++++++++++
 src/foolscap/NEWS                             |  64 ++++++++
 .../{using-pb.xhtml => using-foolscap.xhtml}  |  74 +++++----
 src/foolscap/foolscap/__init__.py             |   2 +-
 src/foolscap/foolscap/call.py                 |  43 ++++-
 src/foolscap/foolscap/pb.py                   |  35 ++++-
 src/foolscap/foolscap/reconnector.py          |  11 +-
 src/foolscap/foolscap/referenceable.py        |  34 ++--
 src/foolscap/foolscap/remoteinterface.py      |  31 +++-
 src/foolscap/foolscap/slicers/allslicers.py   |   4 +-
 src/foolscap/foolscap/slicers/dict.py         |  12 +-
 src/foolscap/foolscap/slicers/list.py         |  23 ++-
 src/foolscap/foolscap/slicers/set.py          | 148 +++++++++++++++---
 src/foolscap/foolscap/slicers/tuple.py        |  31 +++-
 src/foolscap/foolscap/test/test_banana.py     |  83 ++++++++--
 src/foolscap/foolscap/test/test_call.py       |  12 +-
 src/foolscap/foolscap/test/test_gifts.py      | 136 +++++++++++++++-
 src/foolscap/foolscap/test/test_schema.py     |  15 +-
 src/foolscap/foolscap/test/test_tub.py        |  81 +++++++++-
 src/foolscap/misc/dapper/debian/changelog     |  50 +-----
 src/foolscap/misc/dapper/debian/rules         |   2 +-
 src/foolscap/misc/edgy/debian/changelog       |  48 +-----
 src/foolscap/misc/edgy/debian/rules           |   2 +-
 src/foolscap/misc/feisty/debian/changelog     |  48 +-----
 src/foolscap/misc/feisty/debian/rules         |   2 +-
 src/foolscap/misc/sarge/debian/changelog      |  50 +-----
 src/foolscap/misc/sarge/debian/rules          |   2 +-
 src/foolscap/misc/sid/debian/changelog        |  48 +-----
 src/foolscap/misc/sid/debian/rules            |   2 +-
 src/foolscap/misc/testutils/figleaf.py        |   1 -
 30 files changed, 845 insertions(+), 358 deletions(-)
 rename src/foolscap/doc/{using-pb.xhtml => using-foolscap.xhtml} (95%)

diff --git a/src/foolscap/ChangeLog b/src/foolscap/ChangeLog
index 06af6258..bade5f83 100644
--- a/src/foolscap/ChangeLog
+++ b/src/foolscap/ChangeLog
@@ -1,3 +1,112 @@
+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
diff --git a/src/foolscap/NEWS b/src/foolscap/NEWS
index 8ef246fb..f45ecb20 100644
--- a/src/foolscap/NEWS
+++ b/src/foolscap/NEWS
@@ -1,5 +1,69 @@
 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
diff --git a/src/foolscap/doc/using-pb.xhtml b/src/foolscap/doc/using-foolscap.xhtml
similarity index 95%
rename from src/foolscap/doc/using-pb.xhtml
rename to src/foolscap/doc/using-foolscap.xhtml
index 97005d01..9ddc88ce 100644
--- a/src/foolscap/doc/using-pb.xhtml
+++ b/src/foolscap/doc/using-foolscap.xhtml
@@ -101,7 +101,8 @@ d.addCallbacks(gotAnswer, gotError)
 
 <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>
+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>
 
@@ -128,13 +129,13 @@ 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>
+<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>
 
@@ -147,7 +148,7 @@ port is accessibly to the outside world.</p>
 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
+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
@@ -156,10 +157,10 @@ 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
+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. PB uses authenticated Tubs by default.</p>
+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"
@@ -196,7 +197,7 @@ registering it:</p>
 url = tub.registerReference(myserver, "math-service")
 </pre>
 
-<p>This returns the <q>PB URL</q> for your <code>Referenceable</code>. Remote
+<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
@@ -279,6 +280,25 @@ d.addCallbacks(gotReference, gotError)
 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
@@ -333,7 +353,7 @@ the answer is 3
 </pre>
 
 
-<h3>PB URLs</h3>
+<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
@@ -350,9 +370,9 @@ the term, meaning those URLs have the following properties:</p>
   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
+<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>
 
@@ -367,9 +387,9 @@ 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
+<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 PB URL uses the same format
+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>
 
@@ -402,7 +422,7 @@ URL</span>:</p>
 the format <code>pb://ABCD@unix/path/to/socket/NAME</code>, but this needs
 some work)</p>
 
-<p>PB URLs for unauthenticated Tubs, like
+<p>FURLs for unauthenticated Tubs, like
 <code>pbu://example.com:8700/math-server</code>, are formatted as
 follows:</p>
 
@@ -470,13 +490,13 @@ 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
+<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 PBURL that starts with "pbu:").</p>
+relatively short FURL that starts with "pbu:").</p>
 
 <a href="listings/pb3calculator.py" class="py-listing"
 skipLines="2">pb3calculator.py</a>
@@ -766,7 +786,7 @@ suitably unique string (like a URI).</p>
 from foolscap import RemoteInterface, schema
 
 class RIMath(RemoteInterface):
-    __remote_name__ = "RIMath.using-pb.docs.foolscap.twistedmatrix.com"
+    __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
@@ -883,7 +903,7 @@ method is invoked), he will discover that he's holding a fully-functional
 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
+FURLs prevent DNS-spoofing and man-in-the-middle attacks.</span>. He can
 start using this RemoteReference right away:</p>
 
 <pre class="python">
diff --git a/src/foolscap/foolscap/__init__.py b/src/foolscap/foolscap/__init__.py
index e2243f42..ab58d559 100644
--- a/src/foolscap/foolscap/__init__.py
+++ b/src/foolscap/foolscap/__init__.py
@@ -1,6 +1,6 @@
 """Foolscap"""
 
-__version__ = "0.1.3"
+__version__ = "0.1.4"
 
 # here are the primary entry points
 from foolscap.pb import Tub, UnauthenticatedTub, getRemoteURL_TCP
diff --git a/src/foolscap/foolscap/call.py b/src/foolscap/foolscap/call.py
index 8ed3af4b..2f1ccd6a 100644
--- a/src/foolscap/foolscap/call.py
+++ b/src/foolscap/foolscap/call.py
@@ -197,11 +197,14 @@ class InboundDelivery:
 
 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 = {}
@@ -242,6 +245,11 @@ class ArgumentUnslicer(slicer.ScopedUnslicer):
         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)
@@ -261,10 +269,14 @@ class ArgumentUnslicer(slicer.ScopedUnslicer):
             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:
@@ -298,11 +310,13 @@ class ArgumentUnslicer(slicer.ScopedUnslicer):
             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
@@ -311,6 +325,9 @@ class ArgumentUnslicer(slicer.ScopedUnslicer):
         # 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:
@@ -321,12 +338,22 @@ class ArgumentUnslicer(slicer.ScopedUnslicer):
 
     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:
@@ -334,17 +361,25 @@ class ArgumentUnslicer(slicer.ScopedUnslicer):
         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):
@@ -385,6 +420,8 @@ class ArgumentUnslicer(slicer.ScopedUnslicer):
 
 class CallUnslicer(slicer.ScopedUnslicer):
 
+    debug = False
+
     def start(self, count):
         # start=0:reqID, 1:objID, 2:methodname, 3: arguments
         self.stage = 0
@@ -436,7 +473,9 @@ class CallUnslicer(slicer.ScopedUnslicer):
     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
diff --git a/src/foolscap/foolscap/pb.py b/src/foolscap/foolscap/pb.py
index 9f7cfb9d..735f1dbc 100644
--- a/src/foolscap/foolscap/pb.py
+++ b/src/foolscap/foolscap/pb.py
@@ -6,7 +6,7 @@ from twisted.internet import defer, protocol
 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
@@ -242,6 +242,8 @@ class Tub(service.MultiService):
         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
@@ -376,6 +378,15 @@ class Tub(service.MultiService):
         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.")
@@ -386,6 +397,7 @@ class Tub(service.MultiService):
         # 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
@@ -523,8 +535,6 @@ class Tub(service.MultiService):
         @return: a Deferred that fires with the RemoteReference
         """
 
-        assert self.running
-
         if isinstance(sturdyOrURL, SturdyRef):
             sturdy = sturdyOrURL
         else:
@@ -538,12 +548,21 @@ class Tub(service.MultiService):
                             "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
@@ -582,8 +601,12 @@ class Tub(service.MultiService):
          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
 
diff --git a/src/foolscap/foolscap/reconnector.py b/src/foolscap/foolscap/reconnector.py
index d22ab3e8..eace104e 100644
--- a/src/foolscap/foolscap/reconnector.py
+++ b/src/foolscap/foolscap/reconnector.py
@@ -42,17 +42,17 @@ class Reconnector:
     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
@@ -65,7 +65,8 @@ class Reconnector:
         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)
diff --git a/src/foolscap/foolscap/referenceable.py b/src/foolscap/foolscap/referenceable.py
index 283705a7..d940429f 100644
--- a/src/foolscap/foolscap/referenceable.py
+++ b/src/foolscap/foolscap/referenceable.py
@@ -630,8 +630,18 @@ class TheirReferenceUnslicer(slicer.LeafUnslicer):
         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
@@ -727,26 +737,6 @@ class SturdyRef(Copyable, RemoteCopy):
                 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
diff --git a/src/foolscap/foolscap/remoteinterface.py b/src/foolscap/foolscap/remoteinterface.py
index 46df3616..f3fbdc09 100644
--- a/src/foolscap/foolscap/remoteinterface.py
+++ b/src/foolscap/foolscap/remoteinterface.py
@@ -105,7 +105,8 @@ def getRemoteInterface(obj):
         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
@@ -199,7 +200,8 @@ class RemoteMethodSchema:
             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
@@ -361,9 +363,18 @@ class RemoteInterfaceConstraint(OpenerConstraint):
     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):
@@ -387,7 +398,17 @@ class RemoteInterfaceConstraint(OpenerConstraint):
                                 % (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?
diff --git a/src/foolscap/foolscap/slicers/allslicers.py b/src/foolscap/foolscap/slicers/allslicers.py
index aa05f177..afb3b470 100644
--- a/src/foolscap/foolscap/slicers/allslicers.py
+++ b/src/foolscap/foolscap/slicers/allslicers.py
@@ -10,7 +10,7 @@ from foolscap.slicers.unicode import UnicodeSlicer, UnicodeUnslicer
 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
@@ -26,7 +26,7 @@ unused = [
     ListSlicer, ListUnslicer,
     TupleSlicer, TupleUnslicer,
     SetSlicer, SetUnslicer,
-    ImmutableSetSlicer, ImmutableSetUnslicer,
+    FrozenSetSlicer, FrozenSetUnslicer,
     #from foolscap.slicers.set import BuiltinSetSlicer
     DictSlicer, DictUnslicer, OrderedDictSlicer,
     ReplaceVocabSlicer, ReplaceVocabUnslicer,
diff --git a/src/foolscap/foolscap/slicers/dict.py b/src/foolscap/foolscap/slicers/dict.py
index 6cd30c29..23a69ca3 100644
--- a/src/foolscap/foolscap/slicers/dict.py
+++ b/src/foolscap/foolscap/slicers/dict.py
@@ -1,7 +1,7 @@
 # -*- 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
@@ -35,6 +35,7 @@ class DictUnslicer(BaseUnslicer):
         self.d = {}
         self.protocol.setObject(count, self.d)
         self.key = None
+        self._ready_deferreds = []
 
     def checkToken(self, typebyte, size):
         if self.maxKeys != None:
@@ -72,8 +73,8 @@ class DictUnslicer(BaseUnslicer):
         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:
@@ -102,7 +103,10 @@ class DictUnslicer(BaseUnslicer):
         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:
diff --git a/src/foolscap/foolscap/slicers/list.py b/src/foolscap/foolscap/slicers/list.py
index 4e8a0db2..70ad55f8 100644
--- a/src/foolscap/foolscap/slicers/list.py
+++ b/src/foolscap/foolscap/slicers/list.py
@@ -1,7 +1,7 @@
 # -*- 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
@@ -35,8 +35,9 @@ class ListUnslicer(BaseUnslicer):
         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:
@@ -65,15 +66,16 @@ class ListUnslicer(BaseUnslicer):
     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
@@ -86,10 +88,12 @@ class ListUnslicer(BaseUnslicer):
             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)
 
@@ -99,7 +103,10 @@ class ListUnslicer(BaseUnslicer):
         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)
diff --git a/src/foolscap/foolscap/slicers/set.py b/src/foolscap/foolscap/slicers/set.py
index 206507f2..f2afa09c 100644
--- a/src/foolscap/foolscap/slicers/set.py
+++ b/src/foolscap/foolscap/slicers/set.py
@@ -1,7 +1,11 @@
 # -*- 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
@@ -9,34 +13,41 @@ from foolscap.constraint import OpenerConstraint, UnboundedSchema, Any, \
 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):
@@ -45,10 +56,101 @@ class SetUnslicer(ListUnslicer):
         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):
@@ -64,12 +166,8 @@ 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):
diff --git a/src/foolscap/foolscap/slicers/tuple.py b/src/foolscap/foolscap/slicers/tuple.py
index f7ea5657..6469f822 100644
--- a/src/foolscap/foolscap/slicers/tuple.py
+++ b/src/foolscap/foolscap/slicers/tuple.py
@@ -1,6 +1,6 @@
 # -*- 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
@@ -34,6 +34,7 @@ class TupleUnslicer(BaseUnslicer):
         self.finished = False
         self.deferred = Deferred()
         self.protocol.setObject(count, self.deferred)
+        self._ready_deferreds = []
 
     def checkToken(self, typebyte, size):
         if self.constraints == None:
@@ -64,7 +65,8 @@ class TupleUnslicer(BaseUnslicer):
         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)
@@ -81,20 +83,39 @@ class TupleUnslicer(BaseUnslicer):
             # 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)
diff --git a/src/foolscap/foolscap/test/test_banana.py b/src/foolscap/foolscap/test/test_banana.py
index 5951cafe..2c95c3f7 100644
--- a/src/foolscap/foolscap/test/test_banana.py
+++ b/src/foolscap/foolscap/test/test_banana.py
@@ -1597,18 +1597,13 @@ class ThereAndBackAgain(TestBananaMixin, unittest.TestCase):
     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):
@@ -1709,15 +1704,71 @@ class ThereAndBackAgain(TestBananaMixin, unittest.TestCase):
         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):
diff --git a/src/foolscap/foolscap/test/test_call.py b/src/foolscap/foolscap/test/test_call.py
index 04dc4abc..79e19beb 100644
--- a/src/foolscap/foolscap/test/test_call.py
+++ b/src/foolscap/foolscap/test/test_call.py
@@ -9,7 +9,6 @@ if False:
     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
 
@@ -36,7 +35,6 @@ class TestCall(TargetMixin, unittest.TestCase):
         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))
@@ -46,10 +44,14 @@ class TestCall(TargetMixin, unittest.TestCase):
         # 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):
diff --git a/src/foolscap/foolscap/test/test_gifts.py b/src/foolscap/foolscap/test/test_gifts.py
index 3775e657..db6dfd78 100644
--- a/src/foolscap/foolscap/test/test_gifts.py
+++ b/src/foolscap/foolscap/test/test_gifts.py
@@ -1,10 +1,11 @@
 
+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
@@ -24,6 +25,19 @@ def ignoreConnectionDone(f):
     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
@@ -52,9 +66,15 @@ class Gifts(unittest.TestCase):
         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.
@@ -73,6 +93,46 @@ class Gifts(unittest.TestCase):
         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()
@@ -189,6 +249,76 @@ class Gifts(unittest.TestCase):
         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
diff --git a/src/foolscap/foolscap/test/test_schema.py b/src/foolscap/foolscap/test/test_schema.py
index 671b7a73..0c052e78 100644
--- a/src/foolscap/foolscap/test/test_schema.py
+++ b/src/foolscap/foolscap/test/test_schema.py
@@ -2,7 +2,7 @@
 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
@@ -474,6 +474,13 @@ class Arguments(unittest.TestCase):
         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):
@@ -520,9 +527,11 @@ 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)
diff --git a/src/foolscap/foolscap/test/test_tub.py b/src/foolscap/foolscap/test/test_tub.py
index 4b0d31b1..8f36a524 100644
--- a/src/foolscap/foolscap/test/test_tub.py
+++ b/src/foolscap/foolscap/test/test_tub.py
@@ -2,6 +2,7 @@
 
 import os.path
 from twisted.trial import unittest
+from twisted.internet import defer
 
 crypto_available = False
 try:
@@ -10,7 +11,16 @@ 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):
@@ -38,3 +48,72 @@ class TestCertFile(unittest.TestCase):
 
 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
+
diff --git a/src/foolscap/misc/dapper/debian/changelog b/src/foolscap/misc/dapper/debian/changelog
index f3331be3..9699a6df 100644
--- a/src/foolscap/misc/dapper/debian/changelog
+++ b/src/foolscap/misc/dapper/debian/changelog
@@ -1,14 +1,14 @@
-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
 
@@ -16,79 +16,43 @@ 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
 
diff --git a/src/foolscap/misc/dapper/debian/rules b/src/foolscap/misc/dapper/debian/rules
index eb2284d2..8ee04543 100644
--- a/src/foolscap/misc/dapper/debian/rules
+++ b/src/foolscap/misc/dapper/debian/rules
@@ -41,7 +41,7 @@ binary-indep: build install
 	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
diff --git a/src/foolscap/misc/edgy/debian/changelog b/src/foolscap/misc/edgy/debian/changelog
index 93eb7f3a..9699a6df 100644
--- a/src/foolscap/misc/edgy/debian/changelog
+++ b/src/foolscap/misc/edgy/debian/changelog
@@ -1,14 +1,14 @@
-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
 
@@ -16,79 +16,43 @@ 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
 
diff --git a/src/foolscap/misc/edgy/debian/rules b/src/foolscap/misc/edgy/debian/rules
index 3ea18835..252672cc 100644
--- a/src/foolscap/misc/edgy/debian/rules
+++ b/src/foolscap/misc/edgy/debian/rules
@@ -9,7 +9,7 @@ include /usr/share/cdbs/1/class/python-distutils.mk
 
 
 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
diff --git a/src/foolscap/misc/feisty/debian/changelog b/src/foolscap/misc/feisty/debian/changelog
index 93eb7f3a..9699a6df 100644
--- a/src/foolscap/misc/feisty/debian/changelog
+++ b/src/foolscap/misc/feisty/debian/changelog
@@ -1,14 +1,14 @@
-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
 
@@ -16,79 +16,43 @@ 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
 
diff --git a/src/foolscap/misc/feisty/debian/rules b/src/foolscap/misc/feisty/debian/rules
index 3ea18835..252672cc 100644
--- a/src/foolscap/misc/feisty/debian/rules
+++ b/src/foolscap/misc/feisty/debian/rules
@@ -9,7 +9,7 @@ include /usr/share/cdbs/1/class/python-distutils.mk
 
 
 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
diff --git a/src/foolscap/misc/sarge/debian/changelog b/src/foolscap/misc/sarge/debian/changelog
index f3331be3..9699a6df 100644
--- a/src/foolscap/misc/sarge/debian/changelog
+++ b/src/foolscap/misc/sarge/debian/changelog
@@ -1,14 +1,14 @@
-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
 
@@ -16,79 +16,43 @@ 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
 
diff --git a/src/foolscap/misc/sarge/debian/rules b/src/foolscap/misc/sarge/debian/rules
index eb2284d2..8ee04543 100644
--- a/src/foolscap/misc/sarge/debian/rules
+++ b/src/foolscap/misc/sarge/debian/rules
@@ -41,7 +41,7 @@ binary-indep: build install
 	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
diff --git a/src/foolscap/misc/sid/debian/changelog b/src/foolscap/misc/sid/debian/changelog
index 93eb7f3a..9699a6df 100644
--- a/src/foolscap/misc/sid/debian/changelog
+++ b/src/foolscap/misc/sid/debian/changelog
@@ -1,14 +1,14 @@
-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
 
@@ -16,79 +16,43 @@ 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
 
diff --git a/src/foolscap/misc/sid/debian/rules b/src/foolscap/misc/sid/debian/rules
index 3ea18835..252672cc 100644
--- a/src/foolscap/misc/sid/debian/rules
+++ b/src/foolscap/misc/sid/debian/rules
@@ -9,7 +9,7 @@ include /usr/share/cdbs/1/class/python-distutils.mk
 
 
 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
diff --git a/src/foolscap/misc/testutils/figleaf.py b/src/foolscap/misc/testutils/figleaf.py
index a24d4fab..03d8c08d 100644
--- a/src/foolscap/misc/testutils/figleaf.py
+++ b/src/foolscap/misc/testutils/figleaf.py
@@ -168,7 +168,6 @@ class CodeTracer:
         Start recording.
         """
         if not self.started:
-            self.LOG = open("/tmp/flog.out", "w")
             self.started = True
 
             sys.settrace(self.g)
-- 
2.45.2