mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-31 16:36:20 +00:00
update to foolscap-0.1.4
This commit is contained in:
parent
985925d70c
commit
267a068fe4
@ -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
|
||||
|
@ -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
|
||||
|
@ -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">
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
opentype = ("immutable-set",)
|
||||
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):
|
||||
return sets.ImmutableSet(self.list), None
|
||||
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):
|
||||
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):
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,94 +1,58 @@
|
||||
foolscap (0.1.4) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
|
||||
|
||||
foolscap (0.1.3) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
|
||||
|
||||
foolscap (0.1.2+) unstable; urgency=low
|
||||
|
||||
* bump revision while between releases
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
|
||||
|
||||
foolscap (0.1.2) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,94 +1,58 @@
|
||||
foolscap (0.1.4) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
|
||||
|
||||
foolscap (0.1.3) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
|
||||
|
||||
foolscap (0.1.2+) unstable; urgency=low
|
||||
|
||||
* bump revision while between releases
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
|
||||
|
||||
foolscap (0.1.2) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,94 +1,58 @@
|
||||
foolscap (0.1.4) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
|
||||
|
||||
foolscap (0.1.3) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
|
||||
|
||||
foolscap (0.1.2+) unstable; urgency=low
|
||||
|
||||
* bump revision while between releases
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
|
||||
|
||||
foolscap (0.1.2) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,94 +1,58 @@
|
||||
foolscap (0.1.4) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
|
||||
|
||||
foolscap (0.1.3) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
|
||||
|
||||
foolscap (0.1.2+) unstable; urgency=low
|
||||
|
||||
* bump revision while between releases
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
|
||||
|
||||
foolscap (0.1.2) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,94 +1,58 @@
|
||||
foolscap (0.1.4) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Mon, 14 May 2007 22:37:04 -0700
|
||||
|
||||
foolscap (0.1.3) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Wed, 2 May 2007 14:55:49 -0700
|
||||
|
||||
foolscap (0.1.2+) unstable; urgency=low
|
||||
|
||||
* bump revision while between releases
|
||||
|
||||
-- Brian Warner <warner@lothar.com> Fri, 13 Apr 2007 00:22:10 -0700
|
||||
|
||||
foolscap (0.1.2) unstable; urgency=low
|
||||
|
||||
* new release
|
||||
|
||||
-- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user