[Debtorrent-commits] r58 - in /debtorrent/trunk/DebTorrent: BT1/Downloader.py BT1/Storage.py BTcrypto.py ConfigDir.py ConnChoice.py CurrentRateMeasure.py bitfield.py clock.py download_bt1.py

camrdale-guest at users.alioth.debian.org camrdale-guest at users.alioth.debian.org
Tue May 22 07:56:33 UTC 2007


Author: camrdale-guest
Date: Tue May 22 07:56:32 2007
New Revision: 58

URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=58
Log:
More documentation

Modified:
    debtorrent/trunk/DebTorrent/BT1/Downloader.py
    debtorrent/trunk/DebTorrent/BT1/Storage.py
    debtorrent/trunk/DebTorrent/BTcrypto.py
    debtorrent/trunk/DebTorrent/ConfigDir.py
    debtorrent/trunk/DebTorrent/ConnChoice.py
    debtorrent/trunk/DebTorrent/CurrentRateMeasure.py
    debtorrent/trunk/DebTorrent/bitfield.py
    debtorrent/trunk/DebTorrent/clock.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=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Downloader.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Downloader.py Tue May 22 07:56:32 2007
@@ -1,8 +1,15 @@
 # Written by Bram Cohen
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
-
+#
 # $Id$
+
+"""Download pieces from remote peers.
+
+ at type EXPIRE_TIME: C{int}
+ at var EXPIRE_TIME: number of seconds after which disconnected seeds are expired
+
+"""
 
 from DebTorrent.CurrentRateMeasure import Measure
 from DebTorrent.bitfield import Bitfield
@@ -16,8 +23,31 @@
 
 EXPIRE_TIME = 60 * 60
 
-class PerIPStats: 	 
+class PerIPStats:
+    """Statistics relating to downloads from a single peer.
+    
+    @type numgood: C{int}
+    @ivar numgood: the number of good pieces received
+    @type bad: C{dictionary}
+    @ivar bad: keys are piece numbers, values are the number of bad copies
+        of the piece received from the peer
+    @type numconnections: C{int}
+    @ivar numconnections: the number of connections made to the peer
+    @type lastdownload: L{SingleDownload}
+    @ivar lastdownload: the most recent SingleDownload instance
+    @type peerid: C{string}
+    @ivar peerid: the peer's ID
+    
+    """
+    
     def __init__(self, ip):
+        """Initialize the statistics.
+        
+        @type ip: unknown
+        @param ip: the IP address of the peer (not used)
+        
+        """
+        
         self.numgood = 0
         self.bad = {}
         self.numconnections = 0
@@ -25,7 +55,29 @@
         self.peerid = None
 
 class BadDataGuard:
+    """Process good and bad received pieces from a single peer.
+    
+    @type download: L{SingleDownload}
+    @ivar download: the SingleDownload instance
+    @type ip: C{string}
+    @ivar ip: IP address of the peer
+    @type downloader: L{Downloader}
+    @ivar downloader: the Downloader instance
+    @type stats: L{PerIPStats}
+    @ivar stats: the PerIPStats instance
+    @type lastindex: C{int}
+    @ivar lastindex: the last good piece that was received
+    
+    """
+    
     def __init__(self, download):
+        """Initialize the class.
+        
+        @type download: L{SingleDownload}
+        @param download: the SingleDownload instance for the download
+        
+        """
+        
         self.download = download
         self.ip = download.ip
         self.downloader = download.downloader
@@ -33,6 +85,16 @@
         self.lastindex = None
 
     def failed(self, index, bump = False):
+        """Process the failed piece.
+        
+        @type index: C{int}
+        @param index: the piece that failed
+        @type bump: C{boolean}
+        @param bump: whether to increase the interest level in the 
+            L{PiecePicker.PiecePicker} (optional, defaults to False)
+        
+        """
+        
         self.stats.bad.setdefault(index, 0)
         self.downloader.gotbaddata[self.ip] = 1
         self.stats.bad[index] += 1
@@ -47,6 +109,13 @@
             self.downloader.picker.bump(index)
 
     def good(self, index):
+        """Process the successful piece.
+        
+        @type index: C{int}
+        @param index: the piece that succeeded
+        
+        """
+        
         # lastindex is a hack to only increase numgood by one for each good
         # piece, however many chunks come from the connection(s) from this IP
         if index != self.lastindex:
@@ -54,7 +123,49 @@
             self.lastindex = index
 
 class SingleDownload:
+    """Manage downloads from a single peer.
+    
+    @type downloader: L{Downloader}
+    @ivar downloader: the Downloader instance
+    @type connection: unknown
+    @ivar connection: the connection to the peer
+    @type choked: C{boolean}
+    @ivar choked: whether the peer is choking the download
+    @type interested: C{boolean}
+    @ivar interested: whether the peer is interesting
+    @type active_requests: C{list}
+    @ivar active_requests: unknown
+    @type measure: L{DebTorrent.CurrentRateMeasure.Measure}
+    @ivar measure: for measuring the download rate from the peer
+    @type peermeasure: L{DebTorrent.CurrentRateMeasure.Measure}
+    @ivar peermeasure: for measuring the download rate of the peer
+    @type have: L{DebTorrent.bitfield.Bitfield}
+    @ivar have: the bitfield the peer has
+    @type last: C{float}
+    @ivar last: the last time a chunk was received from the peer
+    @type last2: C{float}
+    @ivar last2: the last time a chunk or an unchoke was received
+    @type example_interest: C{int}
+    @ivar example_interest: an example piece to request
+    @type backlog: C{int}
+    @ivar backlog: the current backlog of chunk requests
+    @type ip: C{string}
+    @ivar ip: the IP address of the peer
+    @type guard: L{BadDataGuard}
+    @ivar guard: the guard to use to process pieces
+    
+    """
+    
     def __init__(self, downloader, connection):
+        """Initialize the instance.
+        
+        @type downloader: L{Downloader}
+        @param downloader: the parent Downloader instance
+        @type connection: unknown
+        @param connection: the connection to the peer
+        
+        """
+        
         self.downloader = downloader
         self.connection = connection
         self.choked = True
@@ -71,6 +182,15 @@
         self.guard = BadDataGuard(self)
 
     def _backlog(self, just_unchoked):
+        """Calculate the backlog of chunk requests to the peer.
+        
+        @type just_unchoked: C{boolean}
+        @param just_unchoked: whether the connection was just unchoked
+        @rtype: C{int}
+        @return: the new backlog
+        
+        """
+        
         self.backlog = min(
             2+int(4*self.measure.get_rate()/self.downloader.chunksize),
             (2*just_unchoked)+self.downloader.queue_limit() )
@@ -79,6 +199,7 @@
         return self.backlog
     
     def disconnected(self):
+        """Remove the newly disconnected peer."""
         self.downloader.lost_peer(self)
         if self.have.complete():
             self.downloader.picker.lost_seed()
@@ -92,6 +213,14 @@
         self.guard.download = None
 
     def _letgo(self):
+        """Remove the oustanding requests to the peer.
+        
+        For each active request that was unfulfilled by the peer, inform the 
+        Storage that the request was lost, and send interested messages to any 
+        remaining peers that have the piece.
+        
+        """
+        
         if self.downloader.queued_out.has_key(self):
             del self.downloader.queued_out[self]
         if not self.active_requests:
@@ -119,11 +248,13 @@
                         break
 
     def got_choke(self):
+        """Update the choked status and remove any active requests."""
         if not self.choked:
             self.choked = True
             self._letgo()
 
     def got_unchoke(self):
+        """Update the status and request any needed pieces."""
         if self.choked:
             self.choked = False
             if self.interested:
@@ -131,12 +262,27 @@
             self.last2 = clock()
 
     def is_choked(self):
+        """Get the choked status of the connection.
+        
+        @rtype: C{boolean}
+        @return: whether the peer is choking the connection
+        
+        """
+        
         return self.choked
 
     def is_interested(self):
+        """Get the interest in the peer.
+        
+        @rtype: C{boolean}
+        @return: whether the peer is interesting
+        
+        """
+        
         return self.interested
 
     def send_interested(self):
+        """Send the interested message to the peer."""
         if not self.interested:
             self.interested = True
             self.connection.send_interested()
@@ -144,11 +290,28 @@
                 self.last2 = clock()
 
     def send_not_interested(self):
+        """Send the not interested message to the peer."""
         if self.interested:
             self.interested = False
             self.connection.send_not_interested()
 
     def got_piece(self, index, begin, piece):
+        """Process a received chunk.
+        
+        Add the newly received chunk to the Storage, remove any oustanding
+        requests for it, and request more chunks from the peer.
+        
+        @type index: C{int}
+        @param index: the piece index
+        @type begin: C{int}
+        @param begin: the offset within the piece
+        @type piece: C{string}
+        @param piece: the chunk
+        @rtype: C{boolean}
+        @return: whether the piece was accepted by the Storage (valid)
+        
+        """
+        
         length = len(piece)
         try:
             self.active_requests.remove((index, begin, length))
@@ -187,6 +350,14 @@
         return self.downloader.storage.do_I_have(index)
 
     def _request_more(self, new_unchoke = False):
+        """Request more chunks from the peer.
+        
+        @type new_unchoke: C{boolean}
+        @param new_unchoke: whether this request was the result of a recent
+            unchoke (optional, defaults to False)
+        
+        """
+        
         assert not self.choked
         if self.downloader.endgamemode:
             self.fix_download_endgame(new_unchoke)
@@ -241,6 +412,14 @@
 
 
     def fix_download_endgame(self, new_unchoke = False):
+        """Request more chunks from the peer in endgame mode.
+        
+        @type new_unchoke: C{boolean}
+        @param new_unchoke: whether this request was the result of a recent
+            unchoke (optional, defaults to False)
+        
+        """
+        
         if self.downloader.paused:
             return
         if len(self.active_requests) >= self._backlog(new_unchoke):
@@ -263,6 +442,15 @@
             self.downloader.chunk_requested(length)
 
     def got_have(self, index):
+        """Receive a Have message from the peer.
+        
+        @type index: C{int}
+        @param index: the piece the peer now has
+        @rtype: C{boolean}
+        @return: whether the peer is now a seed
+        
+        """
+        
         self.downloader.totalmeasure.update_rate(self.downloader.storage.piece_lengths[index])
         self.peermeasure.update_rate(self.downloader.storage.piece_lengths[index])
         if not self.have[index]:
@@ -285,6 +473,7 @@
         return self.have.complete()
 
     def _check_interests(self):
+        """Check if the peer is now interesting."""
         if self.interested or self.downloader.paused:
             return
         for i in xrange(len(self.have)):
@@ -295,6 +484,15 @@
                 return
 
     def got_have_bitfield(self, have):
+        """Receive a Bitfield message from the peer.
+        
+        @type have: L{DebTorrent.bitfield.Bitfield}
+        @param have: the bitfield received from the peer
+        @rtype: C{boolean}
+        @return: whether the peer is a seed
+        
+        """
+        
         if self.downloader.storage.am_I_complete() and have.complete():
             if self.downloader.super_seeding:
                 self.connection.send_bitfield(have.tostring()) # be nice, show you're a seed too
@@ -318,9 +516,23 @@
         return have.complete()
 
     def get_rate(self):
+        """Get the current download rate from the peer.
+        
+        @rtype: C{float}
+        @return: the peer's download rate
+        
+        """
+        
         return self.measure.get_rate()
 
     def is_snubbed(self):
+        """Check if the peer is snubbing the download.
+        
+        @rtype: C{boolean}
+        @return: whether the peer is snubbing the connection
+        
+        """
+        
         if ( self.interested and not self.choked
              and clock() - self.last2 > self.downloader.snub_time ):
             for index, begin, length in self.active_requests:
@@ -330,9 +542,109 @@
 
 
 class Downloader:
+    """A collection of all single downloads.
+    
+    @type storage: L{StorageWrapper.StorageWrapper}
+    @ivar storage: the StorageWrapper instance
+    @type picker: L{PiecePicker.PiecePicker}
+    @ivar picker: the PiecePicker instance
+    @type backlog: C{int}
+    @ivar backlog: the maximum number of requests for a single connection
+    @type max_rate_period: C{float}
+    @ivar max_rate_period: maximum amount of time to guess the current 
+            rate estimate represents
+    @type measurefunc: C{method}
+    @ivar measurefunc: the method to call to add downloaded data to the
+            measurement of the download rate
+    @type totalmeasure: L{DebTorrent.CurrentRateMeasure.Measure}
+    @ivar totalmeasure: for measuring the total download rate from all peers
+    @type numpieces: C{int}
+    @ivar numpieces: total number of pieces in the download
+    @type chunksize: C{int}
+    @ivar chunksize: the number of bytes to query for per request
+    @type snub_time: C{float}
+    @ivar snub_time: seconds to wait for data to come in over a connection 
+            before assuming it's semi-permanently choked
+    @type kickfunc: C{method}
+    @ivar kickfunc: method to call to kick a peer
+    @type banfunc: C{method}
+    @ivar banfunc: method to call to ban a peer
+    @type disconnectedseeds: C{dictionary}
+    @ivar disconnectedseeds: unknown
+    @type downloads: C{list} of C{SingleDownload}
+    @ivar downloads: unknown
+    @type perip: C{dictionary}
+    @ivar perip: unknown
+    @type gotbaddata: C{dictionary}
+    @ivar gotbaddata: unknown
+    @type kicked: C{dictionary}
+    @ivar kicked: unknown
+    @type banned: C{dictionary}
+    @ivar banned: unknown
+    @type kickbans_ok: C{boolean}
+    @ivar kickbans_ok: whether to automatically kick/ban peers that send 
+            bad data
+    @type kickbans_halted: C{boolean}
+    @ivar kickbans_halted: unknown
+    @type super_seeding: C{boolean}
+    @ivar super_seeding: unknown
+    @type endgamemode: C{boolean}
+    @ivar endgamemode: unknown
+    @type endgame_queued_pieces: C{list}
+    @ivar endgame_queued_pieces: unknown
+    @type all_requests: C{list}
+    @ivar all_requests: unknown
+    @type discarded: C{long}
+    @ivar discarded: unknown
+    @type download_rate: C{float}
+    @ivar download_rate: the maximum rate to download at
+    @type bytes_requested: C{int}
+    @ivar bytes_requested: the number of bytes in oustanding requests
+    @type last_time: C{float}
+    @ivar last_time: the last time the queue limit was calculated
+    @type queued_out: C{dictionary}
+    @ivar queued_out: unknown
+    @type requeueing: C{boolean}
+    @ivar requeueing: unknown
+    @type paused: C{boolean}
+    @ivar paused: unknown
+    
+    """
+    
     def __init__(self, storage, picker, backlog, max_rate_period,
                  numpieces, chunksize, measurefunc, snub_time,
                  kickbans_ok, kickfunc, banfunc):
+        """Initialize the instance.
+        
+        @type storage: L{StorageWrapper.StorageWrapper}
+        @param storage: the StorageWrapper instance
+        @type picker: L{PiecePicker.PiecePicker}
+        @param picker: the PiecePicker instance
+        @type backlog: C{int}
+        @param backlog: the maximum number of requests for a single connection
+        @type max_rate_period: C{float}
+        @param max_rate_period: maximum amount of time to guess the current 
+            rate estimate represents
+        @type numpieces: C{int}
+        @param numpieces: total number of pieces in the download
+        @type chunksize: C{int}
+        @param chunksize: the number of bytes to query for per request
+        @type measurefunc: C{method}
+        @param measurefunc: the method to call to add downloaded data to the
+            measurement of the download rate
+        @type snub_time: C{float}
+        @param snub_time: seconds to wait for data to come in over a connection 
+            before assuming it's semi-permanently choked
+        @type kickbans_ok: C{boolean}
+        @param kickbans_ok: whether to automatically kick/ban peers that send 
+            bad data
+        @type kickfunc: C{method}
+        @param kickfunc: method to call to kick a peer
+        @type banfunc: C{method}
+        @param banfunc: method to call to ban a peer
+        
+        """
+        
         self.storage = storage
         self.picker = picker
         self.backlog = backlog
@@ -366,10 +678,24 @@
         self.paused = False
 
     def set_download_rate(self, rate):
+        """Set the maximum download rate for all downloads.
+        
+        @type rate: C{float}
+        @param rate: maximum kB/s to download at (0 = no limit)
+        
+        """
+        
         self.download_rate = rate * 1000
         self.bytes_requested = 0
 
     def queue_limit(self):
+        """Get the maximum number of bytes to request.
+        
+        @rtype: C{int}
+        @return: the limit on the number of bytes to request
+        
+        """
+        
         if not self.download_rate:
             return 10e10    # that's a big queue!
         t = clock()
@@ -388,11 +714,27 @@
         return max(int(-self.bytes_requested/self.chunksize),0)
 
     def chunk_requested(self, size):
+        """Add the new request size to the tally.
+        
+        @type size: C{int}
+        @param size: the number of bytes that were requested
+        
+        """
+        
         self.bytes_requested += size
 
     external_data_received = chunk_requested
 
     def make_download(self, connection):
+        """Create a new L{SingleDownload} instance for a new connection.
+        
+        @type connection: unknown
+        @param connection: the connection that was received
+        @rtype: L{SingleDownload}
+        @return: the newly created SingleDownload instance
+        
+        """
+        
         ip = connection.get_ip()
         if self.perip.has_key(ip):
             perip = self.perip[ip]
@@ -406,6 +748,13 @@
         return d
 
     def piece_flunked(self, index):
+        """Request a failed piece from other peers.
+        
+        @type index: C{int}
+        @param index: the piece index that failed
+        
+        """
+        
         if self.paused:
             return
         if self.endgamemode:
@@ -428,9 +777,23 @@
             d.send_interested()
 
     def has_downloaders(self):
+        """Get the number of active downloads.
+        
+        @rtype: C{int}
+        @return: the number of active download connections
+        
+        """
+        
         return len(self.downloads)
 
     def lost_peer(self, download):
+        """Remove a lost peer from the collection of downloads.
+        
+        @type download: L{SingleDownload}
+        @param download: the download peer that was lost
+        
+        """
+        
         ip = download.ip
         self.perip[ip].numconnections -= 1
         if self.perip[ip].lastdownload == download:
@@ -439,7 +802,8 @@
         if self.endgamemode and not self.downloads: # all peers gone
             self._reset_endgame()
 
-    def _reset_endgame(self):            
+    def _reset_endgame(self):
+        """Stop the endgame mode."""
         self.storage.reset_endgame(self.all_requests)
         self.endgamemode = False
         self.all_requests = []
@@ -447,6 +811,13 @@
 
 
     def add_disconnected_seed(self, id):
+        """Save the time of a disconnected seed.
+        
+        @type id: C{string}
+        @param id: the peer ID of the disconnected seed
+        
+        """
+        
 #        if not self.disconnectedseeds.has_key(id):
 #            self.picker.seed_seen_recently()
         self.disconnectedseeds[id]=clock()
@@ -454,6 +825,13 @@
 #	def expire_disconnected_seeds(self):
 
     def num_disconnected_seeds(self):
+        """Expire old disconnected seeds and calculate the recent number.
+        
+        @rtype: C{int}
+        @return: the number of recently seen disconnected seeds
+        
+        """
+        
         # first expire old ones
         expired = []
         for id,t in self.disconnectedseeds.items():
@@ -467,12 +845,26 @@
         # it should be scheduled to run every minute or two.
 
     def _check_kicks_ok(self):
+        """Check whether peers can be kicked for bad data.
+        
+        @rtype: C{boolean}
+        @return: whether it is OK to kick peers
+        
+        """
+        
         if len(self.gotbaddata) > 10:
             self.kickbans_ok = False
             self.kickbans_halted = True
         return self.kickbans_ok and len(self.downloads) > 2
 
     def try_kick(self, download):
+        """If allowed, kick a peer.
+        
+        @type download: L{SingleDownload}
+        @param download: the peer's download connection
+        
+        """
+        
         if self._check_kicks_ok():
             download.guard.download = None
             ip = download.ip
@@ -482,6 +874,13 @@
             self.kickfunc(download.connection)
         
     def try_ban(self, ip):
+        """If allowed, ban a peer.
+        
+        @type ip: C{string}
+        @param ip: the IP address of the peer
+        
+        """
+        
         if self._check_kicks_ok():
             self.banfunc(ip)
             self.banned[ip] = self.perip[ip].peerid
@@ -489,9 +888,22 @@
                 del self.kicked[ip]
 
     def set_super_seed(self):
+        """Enable super-seed mode."""
         self.super_seeding = True
 
     def check_complete(self, index):
+        """Check whether the download is complete.
+        
+        If it is complete, send the piece to the connected seeds and then
+        disconnect them.
+        
+        @type index: C{int}
+        @param index: the last received piece
+        @rtype: C{boolean}
+        @return: whether the download is complete
+        
+        """
+        
         if self.endgamemode and not self.all_requests:
             self.endgamemode = False
         if self.endgame_queued_pieces and not self.endgamemode:
@@ -507,10 +919,25 @@
         return False
 
     def too_many_partials(self):
+        """Check whether there are too many outstanding incomplete pieces.
+        
+        @rtype: C{boolean}
+        @return: if the number of incomplete pieces is greater than half the
+            number of connected downloads
+        
+        """
+        
         return len(self.storage.dirty) > (len(self.downloads)/2)
 
 
     def cancel_piece_download(self, pieces):
+        """Cancel any outstanding requests for the pieces.
+        
+        @type pieces: C{list} of C{int}
+        @param pieces: the list of pieces to cancel
+        
+        """
+        
         if self.endgamemode:
             if self.endgame_queued_pieces:
                 for piece in pieces:
@@ -542,6 +969,13 @@
                 d._check_interests()
 
     def requeue_piece_download(self, pieces = []):
+        """Request more pieces.
+        
+        @type pieces: C{list} of C{int}
+        @param pieces: the list of pieces to requeue
+
+        """
+        
         if self.endgame_queued_pieces:
             for piece in pieces:
                 if not piece in self.endgame_queued_pieces:
@@ -563,6 +997,7 @@
                 d._request_more()
 
     def start_endgame(self):
+        """Switch to endgame mode."""
         assert not self.endgamemode
         self.endgamemode = True
         assert not self.all_requests
@@ -576,6 +1011,13 @@
             d.fix_download_endgame()
 
     def pause(self, flag):
+        """Pause or unpause the download.
+        
+        @type flag: C{boolean}
+        @param flag: whether to pause of unpause.
+        
+        """
+        
         self.paused = flag
         if flag:
             for d in self.downloads:

Modified: debtorrent/trunk/DebTorrent/BT1/Storage.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/Storage.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Storage.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Storage.py Tue May 22 07:56:32 2007
@@ -98,9 +98,6 @@
     @ivar sizes: the desired length of each file (by name)
     @type mtimes: C{dictionary} of {C{string}, C{long}}
     @ivar mtimes: the last modified time of each file (by name)
-    @type lock_file: C{method}
-    @ivar lock_file: locks a file (if file locking is enabled, otherwise does
-        nothing)
     @type lock_file: C{method}
     @ivar lock_file: locks a file (if file locking is enabled, otherwise does
         nothing)

Modified: debtorrent/trunk/DebTorrent/BTcrypto.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BTcrypto.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BTcrypto.py (original)
+++ debtorrent/trunk/DebTorrent/BTcrypto.py Tue May 22 07:56:32 2007
@@ -2,8 +2,21 @@
 # based on code by Uoti Urpala
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
-
+#
 # $Id$
+
+"""Encrypted communication support.
+
+ at type KEY_LENGTH: C{int}
+ at var KEY_LENGTH: the length of the keys to generate
+ at type DH_PRIME: C{long}
+ at var DH_PRIME: a very large prime number
+ at type PAD_MAX: unknown
+ at var PAD_MAX: unknown
+ at type DH_BYTES: C{int}
+ at var DH_BYTES: the number of bytes to use for key lengths
+
+"""
 
 from __future__ import generators   # for python 2.2
 from random import randrange,randint,seed
@@ -32,15 +45,77 @@
 DH_BYTES = 96
 
 def bytetonum(x):
+    """Convert a long number in a string to a number.
+    
+    @type x: C{string}
+    @param x: the data to convert
+    @rtype: C{long}
+    @return: the converted data
+    
+    """
+    
     return long(x.encode('hex'), 16)
 
 def numtobyte(x):
+    """Convert a very large number to a string.
+    
+    @type x: C{long}
+    @param x: the number to convert
+    @rtype: C{string}
+    @return: the string representation of the number
+    
+    """
+    
     x = hex(x).lstrip('0x').rstrip('Ll')
     x = '0'*(192 - len(x)) + x
     return x.decode('hex')
 
 class Crypto:
+    """
+    
+    @type initiator: C{boolean}
+    @ivar initiator: whether the connection was initiated locally
+    @type disable_crypto: C{boolean}
+    @ivar disable_crypto: whether crypto has been disabled
+    @type privkey: C{long}
+    @ivar privkey: randomly generated private key
+    @type pubkey: C{string}
+    @ivar pubkey: public key corresponding to the private key
+    @type keylength: C{int}
+    @ivar keylength: the key length to use
+    @type _VC_pattern: unknown
+    @ivar _VC_pattern: unknown
+    @type S: unknown
+    @ivar S: unknown
+    @type block3a: unknown
+    @ivar block3a: unknown
+    @type block3bkey: unknown
+    @ivar block3bkey: unknown
+    @type block3b: unknown
+    @ivar block3b: unknown
+    @type encrypt: C{method}
+    @ivar encrypt: the method to call to encrypt data
+    @type decrypt: C{method}
+    @ivar decrypt: the method to call to decrypt data
+    @type _read: C{method}
+    @ivar _read: the method to call to read decrypted data
+    @type _write: C{method}
+    @ivar _write: the method to call to write encrypted data
+    
+    """
+    
     def __init__(self, initiator, disable_crypto = False):
+        """Initialize the instance.
+        
+        @type initiator: C{boolean}
+        @param initiator: whether the connection was initiated locally
+        @type disable_crypto: C{boolean}
+        @param disable_crypto: whether crypto has been disabled
+            (optional, default is False)
+        @raise NotImplementedError: if encryption is not installed
+        
+        """
+        
         self.initiator = initiator
         self.disable_crypto = disable_crypto
         if not disable_crypto and not CRYPTO_OK:
@@ -51,17 +126,42 @@
         self._VC_pattern = None
 
     def received_key(self, k):
+        """Process a received key.
+        
+        @type k: C{string}
+        @param k: the key that was received
+        
+        """
+        
         self.S = numtobyte(pow(bytetonum(k), self.privkey, DH_PRIME))
         self.block3a = sha('req1'+self.S).digest()
         self.block3bkey = sha('req3'+self.S).digest()
         self.block3b = None
 
     def _gen_block3b(self, SKEY):
+        """
+        
+        @type SKEY: C{string}
+        @param SKEY: unknown
+        
+        """
+        
         a = sha('req2'+SKEY).digest()
         return ''.join([ chr(ord(a[i])^ord(self.block3bkey[i]))
                          for i in xrange(20) ])
 
     def test_skey(self, s, SKEY):
+        """Check that the encoding matches the encoded value.
+        
+        @type s: C{string}
+        @param s: unknown
+        @type SKEY: C{string}
+        @param SKEY: unknown
+        @rtype: C{boolean}
+        @return: whether the encoding of SKEY matches s
+        
+        """
+        
         block3b = self._gen_block3b(SKEY)
         if block3b != s:
             return False
@@ -71,6 +171,13 @@
         return True
 
     def set_skey(self, SKEY):
+        """
+        
+        @type SKEY: C{string}
+        @param SKEY: unknown
+        
+        """
+        
         if not self.block3b:
             self.block3b = self._gen_block3b(SKEY)
         crypta = ARC4.new(sha('keyA'+self.S+SKEY).digest())
@@ -85,22 +192,60 @@
         self.decrypt('x'*1024)
 
     def VC_pattern(self):
+        """Unknown.
+        
+        @rtype: unknown
+        @return: unknown
+        
+        """
         if not self._VC_pattern:
             self._VC_pattern = self.decrypt('\x00'*8)
         return self._VC_pattern
 
 
     def read(self, s):
+        """Decrypt and pass on the decrypted value.
+        
+        @type s: C{string}
+        @param s: the string to decrypt
+        
+        """
+        
         self._read(self.decrypt(s))
 
     def write(self, s):
+        """Encrypt and pass on the encrypted value.
+        
+        @type s: C{string}
+        @param s: the string to encrypt
+        
+        """
+        
         self._write(self.encrypt(s))
 
     def setrawaccess(self, _read, _write):
+        """Setup the methods to call for reading and writing.
+        
+        @type _read: C{method}
+        @param _read: the method to call for reading decrypted data
+        @type _write: C{method}
+        @param _write: the method to call for writing encrypted data
+        
+        """
+        
         self._read = _read
         self._write = _write
 
     def padding(self):
+        """Generate a random amount of random padding.
+        
+        Generates random bytes, with a random length from 16 to L{PAD_MAX}.
+        
+        @rtype: C{string}
+        @return: the randomly generated padding
+        
+        """
+        
         return urandom(randrange(PAD_MAX-16)+16)
      
         

Modified: debtorrent/trunk/DebTorrent/ConfigDir.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/ConfigDir.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/ConfigDir.py (original)
+++ debtorrent/trunk/DebTorrent/ConfigDir.py Tue May 22 07:56:32 2007
@@ -1,8 +1,21 @@
 #written by John Hoffman
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
-
+#
 # $Id$
+
+"""Manage configuration and cache files.
+
+ at type DIRNAME: C{string}
+ at var DIRNAME: the directory name to use for storing config files
+ at type hexchars: C{string}
+ at var hexchars: the 16 hex characters, in order
+ at type hexmap: C{list}
+ at var hexmap: a mapping from characters to the hex repesentation of the character
+ at type revmap: C{dictionary}
+ at var revmap: the reverse of L{hexmap}
+
+"""
 
 from inifile import ini_write, ini_read
 from bencode import bencode, bdecode
@@ -29,16 +42,45 @@
     revmap[x] = chr(i)
 
 def tohex(s):
+    """Convert a string to hex representation.
+    
+    @type s: C{string}
+    @param s: the string to convert
+    @rtype: C{string}
+    @return: the converted string
+    
+    """
+    
     r = []
     for c in s:
         r.append(hexmap[ord(c)])
     return ''.join(r)
 
 def unhex(s):
+    """Convert a hex representation back to a string.
+    
+    @type s: C{string}
+    @param s: the hex representation of a string
+    @rtype: C{string}
+    @return: the original string
+    
+    """
+    
     r = [ revmap[s[x:x+2]] for x in xrange(0, len(s), 2) ]
     return ''.join(r)
 
-def copyfile(oldpath, newpath): # simple file copy, all in RAM
+def copyfile(oldpath, newpath):
+    """Simple file copy, all in RAM.
+    
+    @type oldpath: C{string}
+    @param oldpath: the file name to copy from
+    @type newpath: C{string}
+    @param newpath: the file name to copy to
+    @rtype: C{boolean}
+    @return: whether the copy was successful
+    
+    """
+    
     try:
         f = open(oldpath,'rb')
         r = f.read()
@@ -64,10 +106,46 @@
 
 
 class ConfigDir:
+    """Manage configuration and cache files.
+    
+    @type config_type: C{string}
+    @ivar config_type: the extension to include in the saved files' names
+    @type dir_root: C{string}
+    @ivar dir_root: the root directory to save config files in
+    @type dir_torrentcache: C{string}
+    @ivar dir_torrentcache: the directory to save torrent files in
+    @type dir_datacache: C{string}
+    @ivar dir_datacache: the directory to save stopped torrent's state in
+    @type dir_piececache: C{string}
+    @ivar dir_piececache: the directory to store temporary piece files in
+    @type configfile: C{string}
+    @ivar configfile: the file name for the saved configuration data
+    @type statefile: C{string}
+    @ivar statefile: the file name for the saved state
+    @type TorrentDataBuffer: C{dictionary}
+    @ivar TorrentDataBuffer: unknown
+    @type config: C{dictionary}
+    @ivar config: the current configuration variables
+    
+    @group Config Handling: setDefaults, checkConfig, loadConfig, saveConfig, getConfig
+    @group State: getState, saveState
+    @group Torrent Files: getTorrents, getTorrentVariations, getTorrent, writeTorrent
+    @group Torrent Data: getTorrentData, writeTorrentData, deleteTorrentData, getPieceDir
+    @group Expire Cache: deleteOldCacheData, deleteOldTorrents
+    
+    """
 
     ###### INITIALIZATION TASKS ######
 
     def __init__(self, config_type = None):
+        """Initialize the instance, create directories and file names.
+        
+        @type config_type: C{string}
+        @param config_type: the extension to include in the saved files' names
+            (optional, default is to use no extension)
+        
+        """
+        
         self.config_type = config_type
         if config_type:
             config_ext = '.'+config_type
@@ -75,6 +153,15 @@
             config_ext = ''
 
         def check_sysvars(x):
+            """Check a system variable to see if it expands to something.
+            
+            @type x: C{string}
+            @param x: the system variable to check
+            @rtype: C{string}
+            @return: the expanded variable, or None if it doesn't expand
+            
+            """
+            
             y = os.path.expandvars(x)
             if y != x and os.path.isdir(y):
                 return y
@@ -116,15 +203,39 @@
     ###### CONFIG HANDLING ######
 
     def setDefaults(self, defaults, ignore=[]):
+        """Set the default values to use for the configuration.
+        
+        @type defaults: C{dictionary}
+        @param defaults: the default config values
+        @type ignore: C{list}
+        @param ignore: the keys in the defaults to ignore
+            (optional, default is to ignore none of them)
+        
+        """
+        
         self.config = defaultargs(defaults)
         for k in ignore:
             if self.config.has_key(k):
                 del self.config[k]
 
     def checkConfig(self):
+        """Check if a config file already exists.
+        
+        @rtype: C{boolean}
+        @return: whether the config file exists
+        
+        """
+        
         return os.path.exists(self.configfile)
 
     def loadConfig(self):
+        """Load a configuration from a config file.
+        
+        @rtype: C{dictionary}
+        @return: the loaded configuration variables
+        
+        """
+        
         try:
             r = ini_read(self.configfile)['']
         except:
@@ -148,6 +259,16 @@
         return self.config
 
     def saveConfig(self, new_config = None):
+        """Sets and writes to the file the new configuration.
+        
+        @type new_config: C{dictionary}
+        @param new_config: the configuration to set and write
+            (optional, default is to use the previously set one)
+        @rtype: boolean
+        @return: whether writing to the file was successful
+        
+        """
+        
         if new_config:
             for k,v in new_config.items():
                 if self.config.has_key(k):
@@ -161,12 +282,27 @@
             return False
 
     def getConfig(self):
+        """Get the current configuration variables.
+        
+        @rtype: C{dictionary}
+        @return: the current configuration variables
+        
+        """
+
         return self.config
 
 
     ###### STATE HANDLING ######
 
     def getState(self):
+        """Get the state from the state file.
+        
+        @rtype: unknown
+        @return: the previosuly saved state, or None if there was no previously
+            saved state
+        
+        """
+        
         try:
             f = open(self.statefile,'rb')
             r = f.read()
@@ -183,6 +319,15 @@
         return r        
 
     def saveState(self, state):
+        """Saves the state to the state file.
+        
+        @type state: unknown
+        @param state: the state to save
+        @rtype: boolean
+        @return: whether the saving was successful
+        
+        """
+
         try:
             f = open(self.statefile,'wb')
             f.write(bencode(state))
@@ -199,6 +344,13 @@
     ###### TORRENT HANDLING ######
 
     def getTorrents(self):
+        """Get a list of the torrents that have cache data.
+        
+        @rtype: C{list} of C{string}
+        @return: the torrent hashes found
+        
+        """
+        
         d = {}
         for f in os.listdir(self.dir_torrentcache):
             f = os.path.basename(f)
@@ -210,6 +362,15 @@
         return d.keys()
 
     def getTorrentVariations(self, t):
+        """Get the torrent variations in the cache data for a given hash.
+
+        @type t: C{string}
+        @param t: the torrent hash to check for
+        @rtype: C{list} of C{int}
+        @return: the variations of the hash found
+        
+        """
+        
         t = tohex(t)
         d = []
         for f in os.listdir(self.dir_torrentcache):
@@ -224,6 +385,17 @@
         return d
 
     def getTorrent(self, t, v = -1):
+        """Get the torrent data for the hash.
+
+        @type t: C{string}
+        @param t: the torrent hash to lookup
+        @type v: C{int}
+        @param v: the variation to get (optional, default is the largest)
+        @rtype: C{dictionary}
+        @return: the torrent metainfo found
+        
+        """
+        
         t = tohex(t)
         if v == -1:
             v = max(self.getTorrentVariations(t))   # potential exception
@@ -241,6 +413,20 @@
         return r
 
     def writeTorrent(self, data, t, v = -1):
+        """Save the torrent data.
+
+        @type data: C{dictionary}
+        @param data: the torrent metainfo
+        @type t: C{string}
+        @param t: the hash of the torrent metainfo
+        @type v: C{int}
+        @param v: the variation to save as, or None for no variation 
+            (optional, default is the next after the largest)
+        @rtype: C{int}
+        @return: the variation used, or None if the write failed
+        
+        """
+
         t = tohex(t)
         if v == -1:
             try:
@@ -264,6 +450,15 @@
     ###### TORRENT DATA HANDLING ######
 
     def getTorrentData(self, t):
+        """Retrieve cached data for a torrent.
+        
+        @type t: C{string}
+        @param t: the info hash to retrieve cached data for
+        @rtype: C{dictionary}
+        @return: the bdecoded cached data
+        
+        """
+        
         if self.TorrentDataBuffer.has_key(t):
             return self.TorrentDataBuffer[t]
         t = os.path.join(self.dir_datacache,tohex(t))
@@ -282,6 +477,17 @@
         return r
 
     def writeTorrentData(self, t, data):
+        """Write cached data for a torrent.
+        
+        @type t: C{string}
+        @param t: the info hash to write cached data for
+        @type data: C{dictionary}
+        @param data: the data to cache
+        @rtype: C{boolean}
+        @return: whether the write was successful
+        
+        """
+
         self.TorrentDataBuffer[t] = data
         try:
             f = open(os.path.join(self.dir_datacache,tohex(t)),'wb')
@@ -298,18 +504,46 @@
         return success
 
     def deleteTorrentData(self, t):
+        """Delete the cached data for a torrent.
+        
+        @type t: C{string}
+        @param t: the info hash to delete the cached data of
+        
+        """
+
         try:
             os.remove(os.path.join(self.dir_datacache,tohex(t)))
         except:
             pass
 
     def getPieceDir(self, t):
+        """Get the directory to save temporary pieces for a torrent.
+        
+        @type t: C{string}
+        @param t: the info hash to get the piece cache data of
+        @rtype: C{string}
+        @return: the directory to save temporary pieces in
+        
+        """
+
         return os.path.join(self.dir_piececache,tohex(t))
 
 
     ###### EXPIRATION HANDLING ######
 
     def deleteOldCacheData(self, days, still_active = [], delete_torrents = False):
+        """Delete old cache data after a period of time.
+        
+        @type days: C{int}
+        @param days: the number of days to delete cached data after
+        @type still_active: C{list} of C{string}
+        @param still_active: the hashes of torrents that are still running
+            (optional, default is to delete all torrent's cached data)
+        @type delete_torrents: C{boolean}
+        @param delete_torrents: whether to delete the torrent files as well
+        
+        """
+        
         if not days:
             return
         exptime = time() - (days*24*3600)
@@ -380,4 +614,14 @@
 
 
     def deleteOldTorrents(self, days, still_active = []):
+        """Delete old cached data and torrents after a period of time.
+        
+        @type days: C{int}
+        @param days: the number of days to delete cached data after
+        @type still_active: C{list} of C{string}
+        @param still_active: the hashes of torrents that are still running
+            (optional, default is to delete all torrent's cached data)
+        
+        """
+        
         self.deleteOldCacheData(days, still_active, True)

Modified: debtorrent/trunk/DebTorrent/ConnChoice.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/ConnChoice.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/ConnChoice.py (original)
+++ debtorrent/trunk/DebTorrent/ConnChoice.py Tue May 22 07:56:32 2007
@@ -1,7 +1,17 @@
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
+#
+# $Id$
 
-# $Id$
+"""Sets the connection choices that are available.
+
+ at type connChoices: C{list} of C{dictionary}
+ at var connChoiceList: Details for each type of connection. Includes limits
+    for each type on the upload rate and number of connections.
+ at type connChoiceList: C{list} of C{string}
+ at var connChoiceList: the names of the connections that are available
+
+"""
 
 connChoices=(
     {'name':'automatic',

Modified: debtorrent/trunk/DebTorrent/CurrentRateMeasure.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/CurrentRateMeasure.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/CurrentRateMeasure.py (original)
+++ debtorrent/trunk/DebTorrent/CurrentRateMeasure.py Tue May 22 07:56:32 2007
@@ -1,13 +1,42 @@
 # Written by Bram Cohen
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
+#
+# $Id$
 
-# $Id$
+"""Measuring rates of download and upload."""
 
 from clock import clock
 
 class Measure:
+    """The measurement of one rate.
+    
+    @type max_rate_period: C{float}
+    @ivar max_rate_period: maximum amount of time to guess the current rate 
+        estimate represents
+    @type ratesince: C{float}
+    @ivar ratesince: the oldest time the rate estimate is for
+    @type last: C{float}
+    @ivar last: the last time the rate was updated
+    @type rate: C{float}
+    @ivar rate: the latest calculated rate
+    @type total: C{long}
+    @ivar total: the total amount that went in to calculating the rate
+    
+    """
+    
     def __init__(self, max_rate_period, fudge = 1):
+        """Initialize the measurement.
+        
+        @type max_rate_period: C{float}
+        @param max_rate_period: maximum amount of time to guess the current 
+            rate estimate represents
+        @type fudge: C{int}
+        @param fudge: time equivalent of writing to kernel-level TCP buffer, 
+            for rate adjustment (optional, defaults to 1)
+        
+        """
+        
         self.max_rate_period = max_rate_period
         self.ratesince = clock() - fudge
         self.last = self.ratesince
@@ -15,6 +44,13 @@
         self.total = 0l
 
     def update_rate(self, amount):
+        """Update the rate with new data.
+        
+        @type amount: C{long}
+        @param amount: the new data to add into the rate calculation
+        
+        """
+        
         self.total += amount
         t = clock()
         self.rate = (self.rate * (self.last - self.ratesince) + 
@@ -24,17 +60,48 @@
             self.ratesince = t - self.max_rate_period
 
     def get_rate(self):
+        """Get the current rate measurement.
+        
+        @rtype: C{float}
+        @return: the current rate
+        
+        """
+        
         self.update_rate(0)
         return self.rate
 
     def get_rate_noupdate(self):
+        """Get the current rate measurement without updating it.
+        
+        @rtype: C{float}
+        @return: the current rate
+        
+        """
+        
         return self.rate
 
     def time_until_rate(self, newrate):
+        """Calculate how long until the rate drops to the target.
+        
+        @type newrate: C{float}
+        @param newrate: the target rate
+        @rtype: C{float}
+        @return: the number of seconds until the rate decreases to the target 
+            rate, or 0 if it's already there (or below it)
+        
+        """
+        
         if self.rate <= newrate:
             return 0
         t = clock() - self.ratesince
         return ((self.rate * t) / newrate) - t
 
     def get_total(self):
+        """Get the total amount used to calculate the rate..
+        
+        @rtype: C{float}
+        @return: the total amount
+        
+        """
+        
         return self.total

Modified: debtorrent/trunk/DebTorrent/bitfield.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/bitfield.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/bitfield.py (original)
+++ debtorrent/trunk/DebTorrent/bitfield.py Tue May 22 07:56:32 2007
@@ -1,8 +1,21 @@
 # Written by Bram Cohen, Uoti Urpala, and John Hoffman
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
-
+#
 # $Id$
+
+"""Manage creation and modification of bitfields.
+
+ at type lookup_table: C{list} of C{tuple} of C{boolean}
+ at var lookup_table: A lookup table for converting characters to a tuple of
+    booleans. Each tuple has 8 booleans, which are true for all the bits
+    in the character that are set.
+ at type reverse_lookup_table: C{dictionary}
+ at var reverse_lookup_table: A reverse mapping of lookup_table. The keys are 
+    the tuples of 8 booleans, which map to values that are the characters
+    the tuples were created from.
+
+"""
 
 try:
     True
@@ -18,6 +31,15 @@
     negsum = lambda a: reduce(lambda x,y: x+(not y), a, 0)
     
 def _int_to_booleans(x):
+    """Convert an integer to a list of booleans.
+    
+    @type x: C{int}
+    @param x: the integer to convert
+    @rtype: C{tuple} of C{list} of C{boolean}
+    @return: the list of booleans
+    
+    """
+    
     r = []
     for i in range(8):
         r.append(bool(x & 0x80))
@@ -33,7 +55,32 @@
 
 
 class Bitfield:
+    """ A bitfield, a indicating the pieces a peer has.
+    
+    @type length: C{int}
+    @ivar length: the length of the bitfield
+    @type array: C{list} of C{boolean}
+    @ivar array: the bitfield
+    @type numfalse: C{int}
+    @ivar numfalse: the number of false entries in the bitfield
+    
+    """
+    
     def __init__(self, length = None, bitstring = None, copyfrom = None):
+        """
+        
+        @type length: C{int}
+        @param length: the length of the bitfield to create
+            (optional, if missing use length of copyfrom)
+        @type bitstring: C{string}
+        @param bitstring: the bitfield string to initialize the bitfield from
+            (optional, default is to initialize all to false)
+        @type copyfrom: L{Bitfield}
+        @param copyfrom: another bitfield to make a copy of
+            (optional, default is to create a new empty one)
+        
+        """
+        
         if copyfrom is not None:
             self.length = copyfrom.length
             self.array = copyfrom.array[:]
@@ -61,17 +108,49 @@
             self.numfalse = length
 
     def __setitem__(self, index, val):
+        """Set one of the bitfield entries.
+        
+        @type index: C{int}
+        @param index: the index to set
+        @type val: C{boolean}
+        @param val: the value to set to
+        
+        """
+        
         val = bool(val)
         self.numfalse += self.array[index]-val
         self.array[index] = val
 
     def __getitem__(self, index):
+        """Get one of the bitfield entries.
+        
+        @type index: C{int}
+        @param index: the index to get
+        @rtype: C{boolean}
+        @return: the value of the bitfield entry
+        
+        """
+        
         return self.array[index]
 
     def __len__(self):
+        """Get the length of the bitfield.
+        
+        @rtype:C{int}
+        @return: the length of the bitfield
+        
+        """
+        
         return self.length
 
     def tostring(self):
+        """Convert the bitfield to a string
+        
+        @rtype: C{string}
+        @return: the bitfield represented as a string
+        
+        """
+        
         booleans = self.array
         t = reverse_lookup_table
         s = len(booleans) % 8
@@ -81,10 +160,18 @@
         return ''.join(r)
 
     def complete(self):
+        """Check if the bitfield is all true.
+        
+        @rtype: C{boolean}
+        @return: whether the bitfield is complete (all true)
+        
+        """
+        
         return not self.numfalse
 
 
 def test_bitfield():
+    """Test the bitfield implementation."""
     try:
         x = Bitfield(7, 'ab')
         assert False

Modified: debtorrent/trunk/DebTorrent/clock.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/clock.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/clock.py (original)
+++ debtorrent/trunk/DebTorrent/clock.py Tue May 22 07:56:32 2007
@@ -1,8 +1,25 @@
 # Written by John Hoffman
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
+#
+# $Id$
 
-# $Id$
+"""Implement wall clock time for Unix systems.
+
+This module implements a clock() function that will return a non-decreasing
+time, regardless of the system it is called on. This is necessary for Unix 
+systems, whose clock() function instead returns the current processor time.
+
+ at type _MAXFORWARD: C{int}
+ at var _MAXFORWARD: the maximum number of seconds to allow the clock
+    to move forward
+ at type _FUDGE: C{int}
+ at var _FUDGE: the fudged time change to use if the clock moved forward more
+    than L{_MAXFORWARD}, or if the clock moved back
+ at type _RTIME: L{RelativeTime}
+ at var _RTIME: the RelativeTime instance to use
+
+"""
 
 from time import *
 import sys
@@ -11,11 +28,35 @@
 _FUDGE = 1
 
 class RelativeTime:
+    """Calculate relative time on Unix systems.
+    
+    @type time: C{float}
+    @ivar time: the last time value measured
+    @type offset: C{float}
+    @ivar offset: the offset to use from the current time values due to
+        any changes made in the clock while the program was running
+    
+    """
+    
     def __init__(self):
+        """Initialize the time values."""
         self.time = time()
         self.offset = 0
 
     def get_time(self):        
+        """Calculate a non-decreasing time.
+        
+        Uses the time() function to calculate non-decreasing time values.
+        Checks to make sure the time values are non-decreasing, and also
+        don't change by more than L{_MAXFORWARD} seconds within a reading.
+        These could occur if the system clock was changed during the running
+        of the program.
+        
+        @rtype: C{float}
+        @return: the current time
+        
+        """
+        
         t = time() + self.offset
         if t < self.time or t > self.time + _MAXFORWARD:
             self.time += _FUDGE
@@ -27,4 +68,14 @@
 if sys.platform != 'win32':
     _RTIME = RelativeTime()
     def clock():
+        """Override the clock() function for Unix systems.
+        
+        This function will return a non-decreasing measure of the current
+        time. This is only used on Unix systems. On Windows systems, the 
+        clock() function from the C{time} module will be used.
+        
+        @rtype: C{float}
+        @return: the relative time from the L{RelativeTime} instance
+        
+        """
         return _RTIME.get_time()

Modified: debtorrent/trunk/DebTorrent/download_bt1.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/download_bt1.py?rev=58&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/download_bt1.py (original)
+++ debtorrent/trunk/DebTorrent/download_bt1.py Tue May 22 07:56:32 2007
@@ -1,8 +1,19 @@
 # Written by Bram Cohen
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
-
+#
 # $Id$
+
+"""Manage a single download.
+
+ at type DEBUG: C{boolean}
+ at var DEBUG: whether to print debugging information
+ at type defaults: C{list} of C{tuple}
+ at var defaults: the default configuration variables, including descriptions
+ at type argslistheader: C{string}
+ at var argslistheader: the header to print before the default config
+
+"""
 
 from zurllib import urlopen
 from urlparse import urlparse
@@ -192,12 +203,46 @@
 
 
 def _failfunc(x):
+    """Default function to use for printing error messages.
+    
+    @type x: C{string}
+    @param x: the error message to print
+    
+    """
+    
     print x
 
-# old-style downloader
 def download(params, filefunc, statusfunc, finfunc, errorfunc, doneflag, cols,
              pathFunc = None, presets = {}, exchandler = None,
              failed = _failfunc, paramfunc = None):
+    """The old-style downloader (no longer used).
+    
+    @type params: unknown
+    @param params: unknown
+    @type filefunc: unknown
+    @param filefunc: unknown
+    @type statusfunc: unknown
+    @param statusfunc: unknown
+    @type finfunc: unknown
+    @param finfunc: unknown
+    @type errorfunc: unknown
+    @param errorfunc: unknown
+    @type doneflag: unknown
+    @param doneflag: unknown
+    @type cols: unknown
+    @param cols: unknown
+    @type pathFunc: unknown
+    @param pathFunc: unknown
+    @type presets: unknown
+    @param presets: unknown
+    @type exchandler: unknown
+    @param exchandler: unknown
+    @type failed: unknown
+    @param failed: unknown
+    @type paramfunc: unknown
+    @param paramfunc: unknown
+    
+    """
 
     try:
         config = parse_params(params, presets)
@@ -266,6 +311,18 @@
 
 
 def parse_params(params, presets = {}):
+    """Parse the command-line parameters.
+    
+    @type params: C{list} of C{string}
+    @param params: the command-line parameters
+    @type presets: C{dictionary}
+    @param presets: the preset values to use (optional)
+    @rtype: C{dictionary}
+    @return: the configuration variables
+    @raise ValueError: if the parameters are not properly specified
+    
+    """
+    
     if len(params) == 0:
         return None
     config, args = parseargs(params, defaults, 0, 1, presets = presets)
@@ -286,10 +343,45 @@
 
 
 def get_usage(defaults = defaults, cols = 100, presets = {}):
+    """Print the usage information for the program.
+    
+    @type defaults: C{list} of C{tuple}
+    @param defaults: the default configuration variables
+        (optional, default is to use L{defaults})
+    @type cols: C{int}
+    @param cols: the width of the print out (optional, default is 100)
+    @type presets: C{dictionary}
+    @param presets: the preset values to use (optional)
+    
+    """
+    
     return (argslistheader + formatDefinitions(defaults, cols, presets))
 
 
 def get_response(file, url, status_to_download, errorfunc):
+    """Get the response data from a metainfo or Packages file.
+    
+    First checks to see if the data is in the Packages file format, and
+    returns the extracted response data if it is. Otherwise, assumes it is
+    a metainfo file and tries to get the response data from it.
+    
+    @type file: C{string}
+    @param file: the file name to use, or None to indicate that the url is
+        to be used
+    @type url: C{string}
+    @param url: the URL to download the metainfo file from
+    @type status_to_download: C{int}
+    @param status_to_download: determines which packages to download based on 
+        /var/lib/dpkg/status (0 = disabled [download all or use --priority], 
+        1 = download updated versions of installed packages,
+        2 = download all installed packages)
+    @type errorfunc: C{function}
+    @param errorfunc: the function to use to print any error messages
+    @rtype: C{dictionary}
+    @return: the metainfo data
+    
+    """
+    
     (response, priority) = get_packages(file, url, status_to_download, errorfunc)
     if response:
         try:
@@ -346,6 +438,25 @@
     return (response, None)
 
 def get_packages(file, url, status_to_download, errorfunc):
+    """Extract the response data from a Packages file.
+    
+    @type file: C{string}
+    @param file: the file name to use, or None to indicate that the url is
+        to be used
+    @type url: C{string}
+    @param url: the URL to download the metainfo file from
+    @type status_to_download: C{int}
+    @param status_to_download: determines which packages to download based on 
+        /var/lib/dpkg/status (0 = disabled [download all or use --priority], 
+        1 = download updated versions of installed packages,
+        2 = download all installed packages)
+    @type errorfunc: C{function}
+    @param errorfunc: the function to use to print any error messages
+    @rtype: C{dictionary}
+    @return: the metainfo data
+    
+    """
+
     encoding = None
 #    if params.has_key('filesystem_encoding'):
 #        encoding = params['filesystem_encoding']
@@ -480,9 +591,151 @@
 
 
 class BT1Download:    
+    """Manage a single download.
+    
+    @type statusfunc: C{method}
+    @ivar statusfunc: the method to call to print status updates
+    @type finfunc: C{method}
+    @ivar finfunc: the method to call when the download is completed
+    @type errorfunc: C{method}
+    @ivar errorfunc: the method to call when an error occurs
+    @type excfunc: C{method}
+    @ivar excfunc: the method to call when an exception occurs
+    @type doneflag: C{threading.Event}
+    @ivar doneflag: the flag that indicates when the program is to be shutdown
+    @type config: C{dictionary}
+    @ivar config: the configuration variables
+    @type response: C{dictionary}
+    @ivar response: the response data from the metainfo file
+    @type infohash: C{string}
+    @ivar infohash: the hash of the info from the response data
+    @type myid: C{string}
+    @ivar myid: the peer ID to use
+    @type rawserver: L{Rawserver.Rawserver}
+    @ivar rawserver: the server controlling the program
+    @type port: C{int}
+    @ivar port: the port being listened to
+    @type info: C{dictionary}
+    @ivar info: the info data from the response data
+    @type pieces: C{list} of C{string}
+    @ivar pieces: the hashes of the pieces
+    @type piece_lengths: C{list} of C{int}
+    @ivar piece_lengths: the lengths of the pieces
+    @type len_pieces: C{int}
+    @ivar len_pieces: the number of pieces
+    @type status_priority: C{dictionary}
+    @ivar status_priority: the file priorities from the dpkg/status file
+    @type argslistheader: C{string}
+    @ivar argslistheader: the header to print before the default config
+    @type unpauseflag: C{threading.Event}
+    @ivar unpauseflag: the flag to unset to pause the download
+    @type downloader: L{BT1.Downloader.Downloader}
+    @ivar downloader: the Downloader instance
+    @type storagewrapper: L{BT1.StorageWrapper.StorageWrapper}
+    @ivar storagewrapper: the StorageWrapper instance
+    @type fileselector: L{BT1.FileSelector.FileSelector}
+    @ivar fileselector: the FileSelector instance
+    @type super_seeding_active: C{boolean}
+    @ivar super_seeding_active: whether the download is in super-seed mode
+    @type filedatflag: C{threading.Event}
+    @ivar filedatflag: unknown
+    @type spewflag: C{threading.Event}
+    @ivar spewflag: unknown
+    @type superseedflag: C{threading.Event}
+    @ivar superseedflag: unknown
+    @type whenpaused: unknown
+    @ivar whenpaused: unknown
+    @type finflag: C{threading.Event}
+    @ivar finflag: unknown
+    @type rerequest: unknown
+    @ivar rerequest: unknown
+    @type tcp_ack_fudge: C{float}
+    @ivar tcp_ack_fudge: the fraction of TCP ACK download overhead to add to 
+        upload rate calculations
+    @type selector_enabled: C{boolean}
+    @ivar selector_enabled: whether to enable the file selector and fast resume function
+    @type appdataobj: L{ConfigDir.ConfigDir}
+    @ivar appdataobj: the configuration and cache directory manager
+    @type excflag: C{threading.Event}
+    @ivar excflag: unknown
+    @type failed: unknown
+    @ivar failed: unknown
+    @type checking: unknown
+    @ivar checking: unknown
+    @type started: unknown
+    @ivar started: unknown
+    @type picker: L{BT1.PiecePicker.PiecePicker}
+    @ivar picker: the PiecePicker instance
+    @type choker: L{BT1.Choker.Choker}
+    @ivar choker: the Choker instance
+    @type filename: C{string}
+    @ivar filename: the save location
+    @type files: C{list} of (C{string}, C{long})
+    @ivar files: the full file names and lengths of all the files in the download
+    @type datalength: C{long}
+    @ivar datalength: the total length of the download
+    @type priority: unknown
+    @ivar priority: unknown
+    @type storage: unknown
+    @ivar storage: unknown
+    @type upmeasure: unknown
+    @ivar upmeasure: unknown
+    @type downmeasure: unknown
+    @ivar downmeasure: unknown
+    @type ratelimiter: unknown
+    @ivar ratelimiter: unknown
+    @type ratemeasure: unknown
+    @ivar ratemeasure: unknown
+    @type ratemeasure_datarejected: unknown
+    @ivar ratemeasure_datarejected: unknown
+    @type connecter: unknown
+    @ivar connecter: unknown
+    @type encoder: unknown
+    @ivar encoder: unknown
+    @type encoder_ban: unknown
+    @ivar encoder_ban: unknown
+    @type httpdownloader: unknown
+    @ivar httpdownloader: unknown
+    @type statistics: unknown
+    @ivar statistics: unknown
+    
+    """
+    
     def __init__(self, statusfunc, finfunc, errorfunc, excfunc, doneflag,
                  config, response, infohash, id, rawserver, port,
                  status_priority = None, appdataobj = None):
+        """Initialize the instance.
+        
+        @type statusfunc: C{method}
+        @param statusfunc: the method to call to print status updates
+        @type finfunc: C{method}
+        @param finfunc: the method to call when the download is completed
+        @type errorfunc: C{method}
+        @param errorfunc: the method to call when an error occurs
+        @type excfunc: C{method}
+        @param excfunc: the method to call when an exception occurs
+        @type doneflag: C{threading.Event}
+        @param doneflag: the flag that indicates when the program is to be shutdown
+        @type config: C{dictionary}
+        @param config: the configuration variables
+        @type response: C{dictionary}
+        @param response: the response data from the metainfo file
+        @type infohash: C{string}
+        @param infohash: the hash of the info from the response data
+        @type id: C{string}
+        @param id: the peer ID to use
+        @type rawserver: L{Rawserver.Rawserver}
+        @param rawserver: the server controlling the program
+        @type port: C{int}
+        @param port: the port being listened to
+        @type status_priority: C{dictionary}
+        @param status_priority: the file priorities, keys are file names, 
+            values are the priority to use (optional, defaults to download all)
+        @type appdataobj: L{ConfigDir.ConfigDir}
+        @param appdataobj: the configuration and cache directory manager
+        
+        """
+        
         self.statusfunc = statusfunc
         self.finfunc = finfunc
         self.errorfunc = errorfunc
@@ -536,6 +789,17 @@
 
 
     def checkSaveLocation(self, loc):
+        """Check whether the download location exists.
+        
+        For multiple files, returns true if a single one exists.
+        
+        @type loc: C{string}
+        @param loc: the save location to check
+        @rtype: C{boolean}
+        @return: whether the location exists.
+        
+        """
+        
         if self.info.has_key('length'):
             return path.exists(loc)
         for x in self.info['files']:
@@ -545,8 +809,31 @@
                 
 
     def saveAs(self, filefunc, pathfunc = None):
+        """Initialize the location to save the download to.
+        
+        @type filefunc: C{method}
+        @param filefunc: the method to call to get the location to save the 
+            download
+        @type pathfunc: C{method}
+        @param pathfunc: the method to call to alert the UI to any possible 
+            change in the download location
+        @rtype: C{string}
+        @return: the location to save to
+        
+        """
+        
         try:
             def make(f, forcedir = False):
+                """Create the parent directories of a file.
+                
+                @type f: C{string}
+                @param f: the file name
+                @type forcedir: C{boolean}
+                @param forcedir: set to True if f is a directory name
+                    (optional, defaults to False)
+                
+                """
+                
                 if not forcedir:
                     f = path.split(f)[0]
                 if f != '' and not path.exists(f):
@@ -614,10 +901,24 @@
     
 
     def getFilename(self):
+        """Get the download location.
+        
+        @rtype: C{string}
+        @return: the download location
+        
+        """
+        
         return self.filename
 
 
     def _finished(self):
+        """Finish the download.
+        
+        Set the files to read-only, update the Choker for seeding, stop the 
+        piece requester.
+        
+        """
+        
         self.finflag.set()
         try:
             self.storage.set_readonly()
@@ -633,11 +934,27 @@
         self.finfunc()
 
     def _data_flunked(self, amount, index):
+        """Process a failed hash check on a piece.
+        
+        @type amount: C{int}
+        @param amount: the amount of failed data
+        @type index: C{int}
+        @param index: the piece that failed
+        
+        """
+        
         self.ratemeasure_datarejected(amount)
         if not self.doneflag.isSet():
             self.errorfunc('piece %d failed hash check, re-downloading it' % index)
 
     def _failed(self, reason):
+        """Stop the failed download.
+        
+        @type reason: C{string}
+        @param reason: the reason for the failure
+        
+        """
+        
         self.failed = True
         self.doneflag.set()
         if reason is not None:
@@ -645,6 +962,22 @@
         
 
     def initFiles(self, old_style = False, statusfunc = None):
+        """Initialize the files for the download.
+        
+        Initialize the priorities, then create the Storage, StorageWrapper, 
+        and FileSelector. Then initialize the StorageWrapper.
+        
+        @type old_style: C{boolean}
+        @param old_style: whether to use the old-style StorageWrapper 
+            initialization (optional, defaults to False)
+        @type statusfunc: C{method}
+        @param statusfunc: the method to use to diplay status updates
+            (optional, defaults to using L{statusfunc}
+        @rtype: C{boolean}
+        @return: whether the initialization was successful
+        
+        """
+        
         if self.doneflag.isSet():
             return None
         if not statusfunc:
@@ -739,20 +1072,63 @@
 
 
     def getCachedTorrentData(self):
+        """Get the cached torrent data from the cache directory.
+        
+        @rtype: C{dictionary}
+        @return: the bdecoded cached data
+        
+        """
+        
         return self.appdataobj.getTorrentData(self.infohash)
 
 
     def _make_upload(self, connection, ratelimiter, totalup):
+        """Create a new Upload instance
+        
+        @type connection: unknown
+        @param connection: the connection to upload on
+        @type ratelimiter: L{BT1.RateLimiter.RateLimiter}
+        @param ratelimiter: the RateLimiter instance to use
+        @type totalup: L{CurrentRateMeasure.Measure}
+        @param totalup: the Measure instance to use to calculate the total 
+            upload rate
+        @rtype: L{BT1.Uploader.Upload}
+        @return: the new Upload instance
+        
+        """
+        
         return Upload(connection, ratelimiter, totalup,
                       self.choker, self.storagewrapper, self.picker,
                       self.config)
 
     def _kick_peer(self, connection):
+        """Disconnect a peer.
+        
+        @type connection: unknown
+        @param connection: the connection of the peer to disconnect
+        
+        """
+        
         def k(connection = connection):
+            """Close a connection.
+            
+            @type connection: unknown
+            @param connection: the connection of the peer to disconnect
+                (optional, defaults to the _kick_peer connection)
+            
+            """
+        
             connection.close()
         self.rawserver.add_task(k,0)
 
     def _ban_peer(self, ip):
+        """Ban a peer from the download.
+        
+        @type ip: C{string}
+        @param ip: the IP address of the peer to ban
+        
+        """
+        
         self.encoder_ban(ip)
 
     def _received_raw_data(self, x):




More information about the Debtorrent-commits mailing list