r189 - in /debtorrent/trunk/DebTorrent: BT1/Downloader.py BT1/Encrypter.py BT1/PiecePicker.py BT1/Rerequester.py download_bt1.py
camrdale-guest at users.alioth.debian.org
camrdale-guest at users.alioth.debian.org
Sat Jul 28 21:54:58 UTC 2007
Author: camrdale-guest
Date: Sat Jul 28 21:54:58 2007
New Revision: 189
URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=189
Log:
You guessed it ... more documentation.
Modified:
debtorrent/trunk/DebTorrent/BT1/Downloader.py
debtorrent/trunk/DebTorrent/BT1/Encrypter.py
debtorrent/trunk/DebTorrent/BT1/PiecePicker.py
debtorrent/trunk/DebTorrent/BT1/Rerequester.py
debtorrent/trunk/DebTorrent/download_bt1.py
Modified: debtorrent/trunk/DebTorrent/BT1/Downloader.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/Downloader.py?rev=189&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Downloader.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Downloader.py Sat Jul 28 21:54:58 2007
@@ -90,7 +90,7 @@
@type index: C{int}
@param index: the piece that failed
@type bump: C{boolean}
- @param bump: whether to increase the interest level in the
+ @param bump: whether to decrease the interest level in the
L{PiecePicker.PiecePicker} (optional, defaults to False)
"""
Modified: debtorrent/trunk/DebTorrent/BT1/Encrypter.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/Encrypter.py?rev=189&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Encrypter.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Encrypter.py Sat Jul 28 21:54:58 2007
@@ -138,11 +138,12 @@
incompletecounter = IncompleteCounter()
-# header, options, download id, my id, [length, message]
-
class Connection:
"""A single, possibly encrypted, connection to a peer.
+ The BitTorrent handshake is in the order: header, options, download id
+ (info hash), peer id, [length, message], ....
+
@type Encoder: L{Encoder}
@ivar Encoder: the collection of all connections
@type connection: L{DebTorrent.SocketHandler.SingleSocket}
Modified: debtorrent/trunk/DebTorrent/BT1/PiecePicker.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/PiecePicker.py?rev=189&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/PiecePicker.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/PiecePicker.py Sat Jul 28 21:54:58 2007
@@ -1,8 +1,10 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Choose pieces to download."""
from random import randrange, shuffle
from DebTorrent.clock import clock
@@ -13,9 +15,96 @@
False = 0
class PiecePicker:
+ """Choose pieces to download.
+
+ @type rarest_first_cutoff: C{int}
+ @ivar rarest_first_cutoff: number of completed piece downloads at
+ which to switch from random piece selection to rarest first
+ @type rarest_first_priority_cutoff: C{int}
+ @ivar rarest_first_priority_cutoff: the number of peers which need to
+ have a piece before other partials take priority over rarest first
+ @type priority_step: C{int}
+ @ivar priority_step: the difference bewteen priority levels 0, 1, and 2
+ @type cutoff: C{int}
+ @ivar cutoff: the number of peers which need to
+ have a piece before other partials take priority over rarest first
+ @type numpieces: C{int}
+ @ivar numpieces: total number of pieces in the download
+ @type started: C{list} of C{int}
+ @ivar started: the pieces that have been requested for download
+ @type totalcount: C{int}
+ @ivar totalcount: the total number of copies of all pieces in non-seeding peers
+ @type numhaves: C{list} of C{int}
+ @ivar numhaves: the number of copies of each piece in non-seeding peers
+ @type priority: C{list} of C{int}
+ @ivar priority: the priority of each piece::
+ -1 -- do not download
+ 0 -- highest priority
+ 1 -- medium priority
+ 2 -- lowest priority
+ @type removed_partials: C{dictionary}
+ @ivar removed_partials: keys are the pieces that were started but then disabled
+ @type crosscount: C{list} of C{int}
+ @ivar crosscount: at each index, the value is the number of pieces that
+ have that many copies in the non-seeding peers
+ @type crosscount2: C{list} of C{int}
+ @ivar crosscount2: at each index, the value is the number of pieces that
+ have that many copies in the non-seeding peers (including this peer)
+ @type has: C{list} of C{int}
+ @ivar has: values are 1 for the pieces that this peer has
+ @type numgot: C{int}
+ @ivar numgot: the number of pieces this peer has
+ @type done: C{boolean}
+ @ivar done: whether the download is complete
+ @type seed_connections: C{dictionary}
+ @ivar seed_connections: connections that have been made in super-seed mode,
+ keys are L{Connecter.Connection}, values are the piece have that was
+ last sent to the peer
+ @type past_ips: C{dictionary}
+ @ivar past_ips: the IPs that have previously connected in super-seed mode,
+ keys are IP addresses, values are the piece have that was last sent to
+ the peer
+ @type seed_time: C{float}
+ @ivar seed_time: the time when the first peer was seen in super-seed mode
+ @type superseed: C{boolean}
+ @ivar superseed: whether we are in super-seed mode
+ @type seeds_connected: C{int}
+ @ivar seeds_connected: the number of connected seeds
+ @type interests: C{list} of C{list} of C{int}
+ @ivar interests: the interest levels, each level is a list of piece indexes
+ that are at that level
+ @type level_in_interests: C{list} of C{int}
+ @ivar level_in_interests: one per piece, the level that each piece is at
+ in the interests
+ @type pos_in_interests: C{list} of C{int}
+ @ivar pos_in_interests: the position within the interest level that each
+ piece is at
+ @type seed_got_haves: C{list} of C{int}
+ @ivar seed_got_haves: the number of copies of pieces in super-seed mode
+
+ """
+
def __init__(self, numpieces,
rarest_first_cutoff = 1, rarest_first_priority_cutoff = 3,
priority_step = 20):
+ """Initialize the instance and the piece interests.
+
+ @type numpieces: C{int}
+ @param numpieces: total number of pieces in the download
+ @type rarest_first_cutoff: C{int}
+ @param rarest_first_cutoff: number of completed piece downloads at
+ which to switch from random piece selection to rarest first
+ (optional, defaults to 1)
+ @type rarest_first_priority_cutoff: C{int}
+ @param rarest_first_priority_cutoff: the number of peers which need to
+ have a piece before other partials take priority over rarest first
+ (optional, defaults to 3)
+ @type priority_step: C{int}
+ @param priority_step: the difference bewteen priority levels 0, 1, and 2
+ (optional, defaults to 20)
+
+ """
+
self.rarest_first_cutoff = rarest_first_cutoff
self.rarest_first_priority_cutoff = rarest_first_priority_cutoff + priority_step
self.priority_step = priority_step
@@ -39,6 +128,7 @@
self._init_interests()
def _init_interests(self):
+ """Initialize the interests in pieces to all not interested."""
self.interests = [[] for x in xrange(self.priority_step)]
self.level_in_interests = [self.priority_step] * self.numpieces
interests = range(self.numpieces)
@@ -51,6 +141,13 @@
def got_have(self, piece):
+ """Process a piece that has a new copy found.
+
+ @type piece: C{int}
+ @param piece: the piece that was copied
+
+ """
+
self.totalcount+=1
numint = self.numhaves[piece]
self.numhaves[piece] += 1
@@ -77,6 +174,13 @@
self._shift_over(piece, self.interests[numint], self.interests[numint + 1])
def lost_have(self, piece):
+ """Process a piece that lost a copy.
+
+ @type piece: C{int}
+ @param piece: the piece that lost a copy
+
+ """
+
self.totalcount-=1
numint = self.numhaves[piece]
self.numhaves[piece] -= 1
@@ -96,6 +200,17 @@
self._shift_over(piece, self.interests[numint], self.interests[numint - 1])
def _shift_over(self, piece, l1, l2):
+ """Remove a piece from an old interest level to a new one.
+
+ @type piece: C{int}
+ @param piece: the piece that is to be moved
+ @type l1: C{list} of C{int}
+ @param l1: the old interest level list
+ @type l2: C{list} of C{int}
+ @param l2: the new interest level list
+
+ """
+
assert self.superseed or (not self.has[piece] and self.priority[piece] >= 0)
parray = self.pos_in_interests
p = parray[piece]
@@ -117,10 +232,12 @@
def got_seed(self):
+ """Process a newly found seed."""
self.seeds_connected += 1
self.cutoff = max(self.rarest_first_priority_cutoff-self.seeds_connected,0)
def became_seed(self):
+ """Process this client becoming a seed."""
self.got_seed()
self.totalcount -= self.numpieces
self.numhaves = [i-1 for i in self.numhaves]
@@ -133,15 +250,35 @@
del self.crosscount2[0]
def lost_seed(self):
+ """Process a lost seed."""
self.seeds_connected -= 1
self.cutoff = max(self.rarest_first_priority_cutoff-self.seeds_connected,0)
def requested(self, piece):
+ """Add a piece that a request has been sent for to the list of started pieces.
+
+ @type piece: C{int}
+ @param piece: the piece that is started
+
+ """
+
if piece not in self.started:
self.started.append(piece)
def _remove_from_interests(self, piece, keep_partial = False):
+ """Remove a piece from the interests.
+
+ @type piece: C{int}
+ @param piece: the piece that is to be removed
+ @type keep_partial: C{boolean}
+ @param keep_partial: if the piece has been started, whether to keep a
+ note of that so that if it becomes interesting again it can be
+ immediately added to the list of started pieces
+ (optional, defaults to False)
+
+ """
+
l = self.interests[self.level_in_interests[piece]]
p = self.pos_in_interests[piece]
assert l[p] == piece
@@ -157,6 +294,13 @@
pass
def complete(self, piece):
+ """Process a successfully received piece.
+
+ @type piece: C{int}
+ @param piece: the piece that was received
+
+ """
+
assert not self.has[piece]
self.has[piece] = 1
self.numgot += 1
@@ -173,6 +317,18 @@
def next(self, haves, wantfunc, complete_first = False):
+ """Choose a piece to request to download next from a peer.
+
+ @type haves: C{list} of C{int}
+ @param haves: the list of pieces that the peer has
+ @type wantfunc: C{method}
+ @param wantfunc: a method that returns True if the piece is desired
+ @type complete_first: C{boolean}
+ @param complete_first: whether to complete a partially dowloaded piece
+ before requsting a new one (optional, defaults to False)
+
+ """
+
cutoff = self.numgot < self.rarest_first_cutoff
complete_first = (complete_first or cutoff) and not haves.complete()
best = None
@@ -203,9 +359,26 @@
def am_I_complete(self):
+ """Determine if the download is complete.
+
+ @rtype: C{boolean}
+ @return: whether the download is complete
+
+ """
+
return self.done
def bump(self, piece):
+ """Move a piece to the end of its interest level.
+
+ Reduces the priority for downloading this piece, unless it is the only
+ one in its level.
+
+ @type piece: C{int}
+ @param piece: the piece that is to be downgraded
+
+ """
+
l = self.interests[self.level_in_interests[piece]]
pos = self.pos_in_interests[piece]
del l[pos]
@@ -218,6 +391,17 @@
pass
def set_priority(self, piece, p):
+ """Adjust the priority for a piece.
+
+ @type piece: C{int}
+ @param piece: the piece that is to be prioritised
+ @type p: C{int}
+ @param p: the new priority for the piece
+ @rtype: C{boolean}
+ @return: whether the downloader should try requesting more
+
+ """
+
if self.superseed:
return False # don't muck with this if you're a superseed
oldp = self.priority[piece]
@@ -265,16 +449,38 @@
return False
def is_blocked(self, piece):
+ """Determine whether a piece is disabled.
+
+ @type piece: C{int}
+ @param piece: the piece that is to be checked
+ @rtype: C{boolean}
+ @return: whether the priority is to not download the piece
+
+ """
+
return self.priority[piece] < 0
def set_superseed(self):
+ """Switch to super-seeding mode."""
assert self.done
self.superseed = True
self.seed_got_haves = [0] * self.numpieces
self._init_interests() # assume everyone is disconnected
def next_have(self, connection, looser_upload):
+ """Determine the next piece to tell a peer we have in super-seed mode.
+
+ @type connection: L{Connecter.Connection}
+ @param connection: the peer connection the have will be sent on
+ @type looser_upload: C{boolean}
+ @param looser_upload: whether to be looser in determining the piece
+ @rtype: C{int}
+ @return: the piece index to send in the have message, or -1 if
+ something has gone wrong and the connection is to be closed
+
+ """
+
if self.seed_time is None:
self.seed_time = clock()
return None
@@ -317,6 +523,13 @@
return -1 # something screwy; terminate connection
def lost_peer(self, connection):
+ """Process a lost peer in super-seed mode.
+
+ @type connection: L{Connecter.Connection}
+ @param connection: the peer connection that was lost
+
+ """
+
olddl = self.seed_connections.get(connection)
if olddl is None:
return
Modified: debtorrent/trunk/DebTorrent/BT1/Rerequester.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/Rerequester.py?rev=189&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Rerequester.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Rerequester.py Sat Jul 28 21:54:58 2007
@@ -2,8 +2,19 @@
# modified for multitracker operation by John Hoffman
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Communicate with a tracker.
+
+ at type mapbase64: C{string}
+ at var mapbase64: the 64 characters to use for a base64 representation
+ at type keys: C{dictionary}
+ at var keys: the key parameters to send to tracker, keys are the announce addresses
+ at type basekeydata: C{string}
+ at var basekeydata: semi-random data to use to create the key
+
+"""
from DebTorrent.zurllib import urlopen, quote
from urlparse import urlparse, urlunparse
@@ -35,12 +46,28 @@
basekeydata = str(getpid()) + repr(time()) + 'tracker'
def add_key(tracker):
+ """Create a random base64 key to send to the tracker.
+
+ @type tracker: C{string}
+ @param tracker: the announce address for the tracker
+
+ """
+
key = ''
for i in sha(basekeydata+tracker).digest()[-6:]:
key += mapbase64[ord(i) & 0x3F]
keys[tracker] = key
def get_key(tracker):
+ """Get the query parameter for the key to send to the tracker.
+
+ @type tracker: C{string}
+ @param tracker: the announce address for the tracker
+ @rtype: C{string}
+ @return: the query parameter to send to the tracker
+
+ """
+
try:
return "&key="+keys[tracker]
except:
@@ -48,19 +75,160 @@
return "&key="+keys[tracker]
class fakeflag:
+ """A fake flag to use if one is not supplied.
+
+ @type state: C{boolean}
+ @ivar state: the current state of the flag
+
+ """
+
def __init__(self, state=False):
+ """Set the new flag.
+
+ @type state: C{boolean}
+ @param state: the initial state of the flag
+
+ """
+
self.state = state
+
def wait(self):
+ """Do nothing."""
pass
+
def isSet(self):
+ """Check if the current state is set.
+
+ @rtype: C{boolean}
+ @return: whether the flag is set
+
+ """
+
return self.state
class Rerequester:
+ """Communicate with a tracker.
+
+ @type sched: C{method}
+ @ivar sched: method to call to schedule future invocation of requester functions
+ @type externalsched: C{method}
+ @ivar externalsched: method to call to schedule future invocation of other functions
+ @type errorfunc: C{method}
+ @ivar errorfunc: method to call when an error occurs
+ @type excfunc: C{method}
+ @ivar excfunc: method to call when an exception occurs
+ @type connect: C{method}
+ @ivar connect: method to call to start connections to new peers
+ @type howmany: C{method}
+ @ivar howmany: method to call to determine how many connections are open
+ @type amount_left: C{method}
+ @ivar amount_left: method to call to determine how much is left to download
+ @type up: C{method}
+ @ivar up: method to call to determine how much has been uploaded
+ @type down: C{method}
+ @ivar down: method to call to determine how much has been downloaded
+ @type upratefunc: C{method}
+ @ivar upratefunc: method to call to determine the current upload rate
+ @type downratefunc: C{method}
+ @ivar downratefunc: method to call to determine the current download rate
+ @type doneflag: C{threading.Event}
+ @ivar doneflag: the flag that indicates when the program is to be shutdown
+ @type unpauseflag: C{threading.Event}
+ @ivar unpauseflag: the flag to unset to pause the download
+ @type seededfunc: C{method}
+ @ivar seededfunc: method to call if the tracker reports the torrent is seeded
+ @type force_rapid_update: C{boolean}
+ @ivar force_rapid_update: whether to do quick tracker updates when requested
+ @type ip: C{string}
+ @ivar ip: IP address to report to the tracker
+ @type minpeers: C{int}
+ @ivar minpeers: minimum number of peers to not do rerequesting
+ @type maxpeers: C{int}
+ @ivar maxpeers: number of peers at which to stop initiating new connections
+ @type interval: C{int}
+ @ivar interval: minimum time to wait between requesting more peers
+ @type timeout: C{int}
+ @ivar timeout: time to wait before assuming that a connection has timed out
+ @type trackerlist: C{list} of C{list} of C{string}
+ @ivar trackerlist: the trackers to connect to
+ @type lastsuccessful: C{string}
+ @ivar lastsuccessful: the last tracker address that was successfully contacted
+ @type rejectedmessage: C{string}
+ @ivar rejectedmessage: the start of the error messages to use when a failure occurs
+ @type url: C{string}
+ @ivar url: the query parameters to send to all trackers
+ @type last: unknown
+ @ivar last: a tracker parameter
+ @type trackerid: unknown
+ @ivar trackerid: a tracker parameter
+ @type announce_interval: C{int}
+ @ivar announce_interval: the tracker-specified announce interval to use
+ @type last_failed: C{boolean}
+ @ivar last_failed: whether the last request was successful
+ @type never_succeeded: C{boolean}
+ @ivar never_succeeded: whether there has ever been a successful request
+ @type errorcodes: C{dictionary}
+ @ivar errorcodes: error codes and messages that have occurred
+ @type lock: L{SuccessLock}
+ @ivar lock: the locks to use to synchronize threaded tracker requests
+ @type special: C{string}
+ @ivar special: a special tracker announce address to send a single request to
+ @type stopped: C{boolean}
+ @ivar stopped: whether the download is stopped
+
+ """
+
def __init__( self, port, myid, infohash, trackerlist, config,
sched, externalsched, errorfunc, excfunc, connect,
howmany, amount_left, up, down, upratefunc, downratefunc,
doneflag, unpauseflag = fakeflag(True),
seededfunc = None, force_rapid_update = False ):
+ """Initialize the instance.
+
+ @type port: C{int}
+ @param port: port to connect to this peer on
+ @type myid: C{string}
+ @param myid: the peer ID to use
+ @type infohash: C{string}
+ @param infohash: the info hash of the torrent being downloaded
+ @type trackerlist: C{list} of C{list} of C{string}
+ @param trackerlist: the trackers to connect to
+ @type config: C{dictionary}
+ @param config: the configuration parameters
+ @type sched: C{method}
+ @param sched: method to call to schedule future invocation of requester functions
+ @type externalsched: C{method}
+ @param externalsched: method to call to schedule future invocation of other functions
+ @type errorfunc: C{method}
+ @param errorfunc: method to call when an error occurs
+ @type excfunc: C{method}
+ @param excfunc: method to call when an exception occurs
+ @type connect: C{method}
+ @param connect: method to call to start connections to new peers
+ @type howmany: C{method}
+ @param howmany: method to call to determine how many connections are open
+ @type amount_left: C{method}
+ @param amount_left: method to call to determine how much is left to download
+ @type up: C{method}
+ @param up: method to call to determine how much has been uploaded
+ @type down: C{method}
+ @param down: method to call to determine how much has been downloaded
+ @type upratefunc: C{method}
+ @param upratefunc: method to call to determine the current upload rate
+ @type downratefunc: C{method}
+ @param downratefunc: method to call to determine the current download rate
+ @type doneflag: C{threading.Event}
+ @param doneflag: the flag that indicates when the program is to be shutdown
+ @type unpauseflag: C{threading.Event}
+ @param unpauseflag: the flag to unset to pause the download
+ @type seededfunc: C{method}
+ @param seededfunc: method to call if the tracker reports the torrent
+ is seeded (optional, defaults to not checking)
+ @type force_rapid_update: C{boolean}
+ @param force_rapid_update: whether to do quick tracker updates when
+ requested (optional, defaults to False)
+
+ """
self.sched = sched
self.externalsched = externalsched
@@ -127,10 +295,12 @@
self.stopped = False
def start(self):
+ """Start the tracker requester."""
self.sched(self.c, self.interval/2)
self.d(0)
def c(self):
+ """Start a periodic general announce request for more peers."""
if self.stopped:
return
if not self.unpauseflag.isSet() and (
@@ -140,9 +310,21 @@
self._c()
def _c(self):
+ """Schedule another general announce request for more peers."""
self.sched(self.c, self.interval)
def d(self, event = 3):
+ """Start a periodic announce request.
+
+ @type event: C{int}
+ @param event: the type of announce request to do (optional, defaults to 3)::
+ 0 -- started
+ 1 -- completed
+ 2 -- stopped
+ 3 -- general announce
+
+ """
+
if self.stopped:
return
if not self.unpauseflag.isSet():
@@ -151,6 +333,7 @@
self.announce(event, self._d)
def _d(self):
+ """Schedule another announce request"""
if self.never_succeeded:
self.sched(self.d, 60) # retry in 60 seconds
elif self.force_rapid_update:
@@ -160,11 +343,39 @@
def hit(self, event = 3):
+ """Start a specific type of announce request for more peers.
+
+ @type event: C{int}
+ @param event: the type of announce request to do (optional, defaults to 3)::
+ 0 -- started
+ 1 -- completed
+ 2 -- stopped
+ 3 -- general announce
+
+ """
+
if not self.unpauseflag.isSet() and (
self.howmany() < self.minpeers or self.force_rapid_update ):
self.announce(event)
def announce(self, event = 3, callback = lambda: None, specialurl = None):
+ """Create an announce request.
+
+ @type event: C{int}
+ @param event: the type of announce request to do (optional, defaults to 3)::
+ 0 -- started
+ 1 -- completed
+ 2 -- stopped
+ 3 -- general announce
+ @type callback: C{method}
+ @param callback: the method to call when the announce is complete
+ (optional, defaults to doing nothing)
+ @type specialurl: C{string}
+ @param specialurl: a special tracker announce address to send this
+ request to (optional, defaults to the regular tracker list)
+
+ """
+
if specialurl is not None:
s = self.url+'&uploaded=0&downloaded=0&left=1' # don't add to statistics
if self.howmany() >= self.maxpeers:
@@ -195,13 +406,36 @@
self.rerequest(s, callback)
- def snoop(self, peers, callback = lambda: None): # tracker call support
+ def snoop(self, peers, callback = lambda: None):
+ """Send an immediate request to the tracker.
+
+ Tracker call support for the tracker-to-tracker communication.
+
+ @type peers: C{int}
+ @param peers: the number of peers to request
+ @type callback: C{method}
+ @param callback: the method to call when the announce is complete
+ (optional, defaults to doing nothing)
+
+ """
+
self.rerequest(self.url
+'&event=stopped&port=0&uploaded=0&downloaded=0&left=1&tracker=1&numwant='
+str(peers), callback)
def rerequest(self, s, callback):
+ """Start a threaded request to a tracker.
+
+ Uses the L{SuccessLock} to make sure concurrent requests are not allowed.
+
+ @type s: C{string}
+ @param s: the query to add to the URL for the request
+ @type callback: C{method}
+ @param callback: the method to call when the announce is complete
+
+ """
+
if not self.lock.isfinished(): # still waiting for prior cycle to complete??
def retry(self = self, s = s, callback = callback):
self.rerequest(s, callback)
@@ -213,6 +447,15 @@
rq.start()
def _rerequest(self, s, callback):
+ """Try all the trackers in the list for an announce request.
+
+ @type s: C{string}
+ @param s: the query to add to the URL for the request
+ @type callback: C{method}
+ @param callback: the method to call when the announce is complete
+
+ """
+
try:
def fail (self = self, callback = callback):
self._fail(callback)
@@ -244,6 +487,13 @@
def _fail(self, callback):
+ """Process the failed request.
+
+ @type callback: C{method}
+ @param callback: the method to call when the announce is complete
+
+ """
+
if ( (self.upratefunc() < 100 and self.downratefunc() < 100)
or not self.amount_left() ):
for f in ['rejected', 'bad_data', 'troublecode']:
@@ -260,6 +510,17 @@
def rerequest_single(self, t, s, callback):
+ """Start a threaded request to a single tracker and wait for it to complete.
+
+ @type t: C{string}
+ @param t: the announce address of the tracker to contact
+ @type s: C{string}
+ @param s: the query to add to the URL for the request
+ @type callback: C{method}
+ @param callback: the method to call when the announce is complete
+
+ """
+
l = self.lock.set()
rq = Thread(target = self._rerequest_single, args = [t, s+get_key(t), l, callback])
rq.setDaemon(False)
@@ -281,6 +542,19 @@
def _rerequest_single(self, t, s, l, callback):
+ """Perform a request to a single tracker.
+
+ @type t: C{string}
+ @param t: the announce address of the tracker to contact
+ @type s: C{string}
+ @param s: the query to add to the URL for the request
+ @type l: C{long}
+ @param l: the current hold on the lock
+ @type callback: C{method}
+ @param callback: the method to call when the announce is complete
+
+ """
+
try:
closer = [None]
def timedout(self = self, l = l, closer = closer):
@@ -354,6 +628,15 @@
def postrequest(self, r, callback):
+ """Process the returned request from a single tracker.
+
+ @type r: C{dictionary}
+ @param r: the bdecoded data returned by the tracker
+ @type callback: C{method}
+ @param callback: the method to call when the request is complete
+
+ """
+
if r.has_key('warning message'):
self.errorfunc('warning from tracker - ' + r['warning message'])
self.announce_interval = r.get('interval', self.announce_interval)
@@ -401,6 +684,13 @@
callback()
def exception(self, callback):
+ """Process an exception that occurred during the request..
+
+ @type callback: C{method}
+ @param callback: the method to call when the request is complete
+
+ """
+
data = StringIO()
print_exc(file = data)
def r(s = data.getvalue(), callback = callback):
@@ -413,7 +703,25 @@
class SuccessLock:
+ """Locks to synchronize threaded requests to trackers.
+
+ @type lock: C{threading.Lock)
+ @ivar lock: lock to ensure no concurrent access to this object
+ @type pause: C{threading.Lock)
+ @ivar pause: lock to synchronize sending requests to trackers
+ @type code: C{long}
+ @ivar code: a unique code sent to each setter of the lock
+ @type success: C{boolean}
+ @ivar success: whether the request was successful
+ @type finished: C{boolean}
+ @ivar finished: whether the request is complete
+ @type first: C{boolean}
+ @ivar first: whether the first trip has occurred yet
+
+ """
+
def __init__(self):
+ """Initialize the instance."""
self.lock = Lock()
self.pause = Lock()
self.code = 0L
@@ -421,10 +729,18 @@
self.finished = True
def reset(self):
+ """Reset the instance to it's initial state."""
self.success = False
self.finished = False
def set(self):
+ """Set the lock for a request.
+
+ @rtype: C{long}
+ @return: the unique identifier code for this request
+
+ """
+
self.lock.acquire()
if not self.pause.locked():
self.pause.acquire()
@@ -434,6 +750,15 @@
return self.code
def trip(self, code, s = False):
+ """Interrupt a not yet finished request.
+
+ @type code: C{long}
+ @param code: the unique identifier code for the request
+ @type s: C{boolean}
+ @param s: whether to set the request as successful
+
+ """
+
self.lock.acquire()
try:
if code == self.code and not self.finished:
@@ -447,19 +772,35 @@
self.lock.release()
def give_up(self):
+ """Terminate a request unsuccessfully."""
self.lock.acquire()
self.success = False
self.finished = True
self.lock.release()
def wait(self):
+ """Wait for the current request to complete."""
self.pause.acquire()
def unwait(self, code):
+ """Stop waiting for a request to complete.
+
+ @type code: C{long}
+ @param code: the unique identifier code for the request
+
+ """
+
if code == self.code and self.pause.locked():
self.pause.release()
def isfinished(self):
+ """Check if the current request is complete.
+
+ @rtype: C{boolean}
+ @return: whether the request is complete
+
+ """
+
self.lock.acquire()
x = self.finished
self.lock.release()
Modified: debtorrent/trunk/DebTorrent/download_bt1.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/download_bt1.py?rev=189&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/download_bt1.py (original)
+++ debtorrent/trunk/DebTorrent/download_bt1.py Sat Jul 28 21:54:58 2007
@@ -1109,7 +1109,8 @@
"""Start the tracker requester.
@type seededfunc: C{method}
- @param seededfunc: unknown
+ @param seededfunc: method to call if the tracker reports the torrent
+ is seeded (optional, defaults to not checking)
@type force_rapid_update: C{boolean}
@param force_rapid_update: whether to do quick tracker updates when
requested (optional, defaults to False)
More information about the Debtorrent-commits
mailing list