r96 - in /debtorrent/branches/http-listen/DebTorrent: BT1/AptListener.py BT1/HTTPDownloader.py ServerPortHandler.py __init__.py launchmanycore.py
camrdale-guest at users.alioth.debian.org
camrdale-guest at users.alioth.debian.org
Mon Jun 11 07:05:56 UTC 2007
Author: camrdale-guest
Date: Mon Jun 11 07:05:55 2007
New Revision: 96
URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=96
Log:
Some more documentation.
Modified:
debtorrent/branches/http-listen/DebTorrent/BT1/AptListener.py
debtorrent/branches/http-listen/DebTorrent/BT1/HTTPDownloader.py
debtorrent/branches/http-listen/DebTorrent/ServerPortHandler.py
debtorrent/branches/http-listen/DebTorrent/__init__.py
debtorrent/branches/http-listen/DebTorrent/launchmanycore.py
Modified: debtorrent/branches/http-listen/DebTorrent/BT1/AptListener.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/BT1/AptListener.py?rev=96&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/BT1/AptListener.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/BT1/AptListener.py Mon Jun 11 07:05:55 2007
@@ -389,7 +389,7 @@
"""Proxy the download of a file from a mirror.
@type path: C{list} of C{string}
- @param hash: the path of the file to download, starting with the mirror name
+ @param path: the path of the file to download, starting with the mirror name
@rtype: (C{int}, C{string}, C{dictionary}, C{string})
@return: the HTTP status code, status message, headers, and bencoded
metainfo file
Modified: debtorrent/branches/http-listen/DebTorrent/BT1/HTTPDownloader.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/BT1/HTTPDownloader.py?rev=96&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/BT1/HTTPDownloader.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/BT1/HTTPDownloader.py Mon Jun 11 07:05:55 2007
@@ -394,7 +394,7 @@
@ivar picker: the piece choosing instance
@type rawserver: L{Debtorrent.RawServer.RawServer}
@ivar rawserver: the server
- @type finflag: C{Threading.Event}
+ @type finflag: C{threading.Event}
@ivar finflag: the flag indicating when the download is complete
@type errorfunc: C{method}
@ivar errorfunc: the method to call when an error occurs
@@ -432,7 +432,7 @@
@param picker: the piece choosing instance
@type rawserver: L{Debtorrent.RawServer.RawServer}
@param rawserver: the server
- @type finflag: C{Threading.Event}
+ @type finflag: C{threading.Event}
@param finflag: the flag indicating when the download is complete
@type errorfunc: C{method}
@param errorfunc: the method to call when an error occurs
Modified: debtorrent/branches/http-listen/DebTorrent/ServerPortHandler.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/ServerPortHandler.py?rev=96&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/ServerPortHandler.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/ServerPortHandler.py Mon Jun 11 07:05:55 2007
@@ -1,8 +1,15 @@
# Written by John Hoffman
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Wrappers to handle multiple torrent downloads.
+
+ at type default_task_id: C{mutable}
+ at var default_task_id: the default task ID to use for scheduling all unspecified tasks
+
+"""
from cStringIO import StringIO
#from RawServer import RawServer
@@ -18,7 +25,47 @@
default_task_id = []
class SingleRawServer:
+ """Simplified Server to handle one of many torrents.
+
+ This class provides a wrapper around a master L{RawServer.RawServer}
+ instance, processing requests with the same interface and passing them
+ on to the master server.
+
+ @type info_hash: C{string}
+ @ivar info_hash: the torrent infohash this Server is responsible for
+ @type doneflag: C{threading.Event}
+ @ivar doneflag: flag to indicate this torrent is being shutdown
+ @type protocol: C{string}
+ @ivar protocol: the name of the communication protocol
+ @type multihandler: L{MultiHandler}
+ @ivar multihandler: the collection of all individual simplified servers
+ @type rawserver: L{RawServer.RawServer}
+ @ivar rawserver: the master Server instance
+ @type finished: C{boolean}
+ @ivar finished: whether this torrent has been shutdown
+ @type running: C{boolean}
+ @ivar running: whether this torrent has been started and is running
+ @type handler: unknown
+ @ivar handler: the data handler to use to process data received on the connection
+ @type taskqueue: C{list}
+ @ivar taskqueue: unknown
+
+ """
+
def __init__(self, info_hash, multihandler, doneflag, protocol):
+ """Initialize the instance.
+
+ @type info_hash: C{string}
+ @param info_hash: the torrent infohash this Server is responsible for
+ @type multihandler: L{MultiHandler}
+ @param multihandler: the collection of all individual simplified servers
+ @type doneflag: C{threading.Event}
+ @param doneflag: flag to indicate this torrent is being shutdown
+ @type protocol: C{string}
+ @param protocol: the name of the communication protocol
+
+ """
+
self.info_hash = info_hash
self.doneflag = doneflag
self.protocol = protocol
@@ -30,10 +77,12 @@
self.taskqueue = []
def shutdown(self):
+ """Tell the collection to shutdown this torrent."""
if not self.finished:
self.multihandler.shutdown_torrent(self.info_hash)
def _shutdown(self):
+ """Shutdown this torrent."""
if not self.finished:
self.finished = True
self.running = False
@@ -43,6 +92,20 @@
def _external_connection_made(self, c, options, already_read,
encrypted = None ):
+ """Processes a new socket connection to this torrent.
+
+ @type c: unknown
+ @param c: the new connection
+ @type options: unknown
+ @param options: the protocol options the connected peer supports
+ @type already_read: C{string}
+ @param already_read: the data that has already been read from the connection
+ @type encrypted: L{BTcrypto.Crypto}
+ @param encrypted: the Crypto instance to use to encrypt this connections
+ communication (optional, defaults to None)
+
+ """
+
if self.running:
c.set_handler(self.handler)
self.handler.externally_handshaked_connection_made(
@@ -51,6 +114,17 @@
### RawServer functions ###
def add_task(self, func, delay=0, id = default_task_id):
+ """Passes a delayed call to a method on to the master Server.
+
+ @type func: C{method}
+ @param func: the method to call
+ @type delay: C{int}
+ @param delay: the number of seconds to delay before calling
+ @type id: C{mutable}
+ @param id: the ID of the task
+
+ """
+
if id is default_task_id:
id = self.info_hash
if not self.finished:
@@ -60,6 +134,18 @@
# pass # not handled here
def start_connection(self, dns, handler = None):
+ """Tell the master Server to start a new connection to a peer.
+
+ @type dns: (C{string}, C{int})
+ @param dns: the IP address and port number to contact the peer on
+ @type handler: unknown
+ @param handler: the data handler to use to process data on the connection
+ (optional, defaults to the L{handler})
+ @rtype: L{SocketHandler.SingleSocket}
+ @return: the new connection made to the peer
+
+ """
+
if not handler:
handler = self.handler
c = self.rawserver.start_connection(dns, handler)
@@ -69,19 +155,89 @@
# pass # don't call with this
def start_listening(self, handler):
+ """Start the Server listening (but not forever).
+
+ @type handler: unknown
+ @param handler: the default handler to call when data comes in
+ @rtype: C{method}
+ @return: the method to call to shutdown the torrent download
+
+ """
+
self.handler = handler
self.running = True
return self.shutdown # obviously, doesn't listen forever
def is_finished(self):
+ """Check if the torrent download has been shutdown.
+
+ @rtype: C{boolean}
+ @return: whether the torrent has been shutdown
+
+ """
+
return self.finished
def get_exception_flag(self):
+ """Get the master Server's exception flag.
+
+ @rtype: C{threading.Event}
+ @return: the flag used to indicate exceptions
+
+ """
+
return self.rawserver.get_exception_flag()
-class NewSocketHandler: # hand a new socket off where it belongs
+class NewSocketHandler:
+ """Read the handshake and hand a new socket connection off to where it belongs.
+
+ This class wraps some of the functionality of the
+ L{BT1.Encrypter.Connection} class. It will receive connections from
+ the Server, read the protocol handshake, assign them to the proper
+ torrent server, and pass the connection on to the Encrypter Connection.
+
+ @type multihandler: L{MultiHandler}
+ @ivar multihandler: the collection of all torrent Servers
+ @type connection: unknown
+ @ivar connection: the connection to handle
+ @type closed: C{boolean}
+ @ivar closed: whether the connection has been closed
+ @type buffer: C{string}
+ @ivar buffer: the buffer of unprocessed data received on the connection
+ @type complete: C{boolean}
+ @ivar complete: whether the handshake is complete
+ @type read: C{method}
+ @ivar read: the method to call to read data from the connection
+ @type write: C{method}
+ @ivar write: the method to call to write data to the connnection
+ @type next_len: C{int}
+ @ivar next_len: the length of the protocol name header in the connection
+ @type next_func: C{method}
+ @ivar next_func: the method to call to read the protocol name from the connection
+ @type protocol: C{string}
+ @ivar protocol: the protocol name used by the connection
+ @type encrypted: C{boolean}
+ @ivar encrypted: whether the connection is encrypted
+ @type encrypter: L{BTcrypto.Crypto}
+ @ivar encrypter: the encrypter to use for the connection
+ @type _max_search: C{int}
+ @ivar _max_search: the number of remaining bytes to search for the pattern
+ @type options: C{string}
+ @ivar options: the protocol options read from the connection
+
+ """
+
def __init__(self, multihandler, connection):
+ """Initialize the instance.
+
+ @type multihandler: L{MultiHandler}
+ @param multihandler: the collection of all torrent Servers
+ @type connection: unknown
+ @param connection: the new connection to handle
+
+ """
+
self.multihandler = multihandler
self.connection = connection
connection.set_handler(self)
@@ -94,10 +250,12 @@
self.multihandler.rawserver.add_task(self._auto_close, 30)
def _auto_close(self):
+ """Automatically close the connection if it is not fully connected."""
if not self.complete:
self.close()
def close(self):
+ """Close the connection."""
if not self.closed:
self.connection.close()
self.closed = True
@@ -105,12 +263,32 @@
# copied from Encrypter and modified
def _read_header(self, s):
+ """Check if the protocol header matches.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{int}, C{method}
+ @return: the next length to read and method to call with the data
+ (or None if something went wrong)
+
+ """
+
if s == chr(len(protocol_name))+protocol_name:
self.protocol = protocol_name
return 8, self.read_options
return None
def read_header(self, s):
+ """Process the (possibly encrypted) protocol header from the connection.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{int}, C{method}
+ @return: the next length to read and method to call with the data
+ (or None if something went wrong)
+
+ """
+
if self._read_header(s):
if self.multihandler.config['crypto_only']:
return None
@@ -123,12 +301,32 @@
return self.encrypter.keylength, self.read_crypto_header
def read_crypto_header(self, s):
+ """Start to read an encrypted header from the connection.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{int}, C{method}
+ @return: the next length to read and method to call with the data
+
+ """
+
self.encrypter.received_key(s)
self.write(self.encrypter.pubkey+self.encrypter.padding())
self._max_search = 520
return 0, self.read_crypto_block3a
def _search_for_pattern(self, s, pat):
+ """Search for a pattern in the initial connection data.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @type pat: C{string}
+ @param pat: the data to search for
+ @rtype: C{boolean}
+ @return: whether the pattern was found
+
+ """
+
p = s.find(pat)
if p < 0:
self._max_search -= len(s)+1-len(pat)
@@ -141,11 +339,32 @@
return True
def read_crypto_block3a(self, s):
+ """Find the block3a crypto information in the connection.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{int}, C{method}
+ @return: the next length to read and method to call with the data
+
+ """
+
if not self._search_for_pattern(s,self.encrypter.block3a):
return -1, self.read_crypto_block3a # wait for more data
return 20, self.read_crypto_block3b
def read_crypto_block3b(self, s):
+ """Process the block3b crypto information in the connection.
+
+ Passes the connection off to the appropriate torrent's Server if the
+ correct block is found.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{boolean}
+ @return: whether the crypto block was found
+
+ """
+
for k in self.multihandler.singlerawservers.keys():
if self.encrypter.test_skey(s,k):
self.multihandler.singlerawservers[k]._external_connection_made(
@@ -155,10 +374,30 @@
return None
def read_options(self, s):
+ """Process the protocol options from the connection.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{int}, C{method}
+ @return: the next length to read and method to call with the data
+
+ """
+
self.options = s
return 20, self.read_download_id
def read_download_id(self, s):
+ """Read the torrent infohash from the connection.
+
+ Passes the connection off to the appropriate torrent's Server.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{boolean}
+ @return: whether a torrent was found to assign the connection to
+
+ """
+
if self.multihandler.singlerawservers.has_key(s):
if self.multihandler.singlerawservers[s].protocol == self.protocol:
self.multihandler.singlerawservers[s]._external_connection_made(
@@ -168,15 +407,56 @@
def read_dead(self, s):
+ """Do nothing.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+ @rtype: C{none}
+ @return: None
+
+ """
+
return None
def data_came_in(self, garbage, s):
+ """Process the read data from the connection.
+
+ @type garbage: unknown
+ @param garbage: thrown away
+ @type s: C{string}
+ @param s: the data read from the conection
+
+ """
+
self.read(s)
def _write_buffer(self, s):
+ """Add the read data from the connection back onto the start of the buffer.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+
+ """
+
self.buffer = s+self.buffer
def _read(self, s):
+ """Process the data read from the connection.
+
+ Processes incoming data on the connection. The data is bufferred, then
+ the L{next_func} method is called with the L{next_len} amount of the
+ data. If it returns None, the connection is closed. If it returns True,
+ the connection handshake is complete and the connection is established.
+ Otherwise it returns C{int},C{method}, which is the next length to read
+ and method to call with the data. If the length is 0, it will read all
+ the available data. If the length is -1 it will wait for more data to
+ caome in.
+
+ @type s: C{string}
+ @param s: the data read from the conection
+
+ """
+
self.buffer += s
while True:
if self.closed:
@@ -209,13 +489,56 @@
def connection_flushed(self, ss):
+ """Do nothing.
+
+ @type ss: unknown
+ @param ss: the connection that was flushed
+
+ """
+
pass
def connection_lost(self, ss):
+ """Close the lost connection.
+
+ @type ss: unknown
+ @param ss: the connection that was lost
+
+ """
+
self.closed = True
class MultiHandler:
+ """Collection of Servers/Port Handlers for multiple torrents.
+
+ @type rawserver: L{RawServer.RawServer}
+ @ivar rawserver: the master Server
+ @type masterdoneflag: C{threading.Event}
+ @ivar masterdoneflag: the flag to indicate stopping to the master Server
+ @type config: C{dictionary}
+ @ivar config: the configuration parameters
+ @type singlerawservers: C{dictionary}
+ @ivar singlerawservers: keys are torrent infohash strings, values are
+ individual L{SingleRawServer} for the torrents
+ @type connections: C{dictionary}
+ @ivar connections: unknown
+ @type taskqueues: C{dictionary}
+ @ivar taskqueues: unknown
+
+ """
+
def __init__(self, rawserver, doneflag, config):
+ """Initialize the instance.
+
+ @type rawserver: L{RawServer.RawServer}
+ @param rawserver: the master Server
+ @type doneflag: C{threading.Event}
+ @param doneflag: the flag to indicate stopping to the master Server
+ @type config: C{dictionary}
+ @param config: the configuration parameters
+
+ """
+
self.rawserver = rawserver
self.masterdoneflag = doneflag
self.config = config
@@ -224,15 +547,37 @@
self.taskqueues = {}
def newRawServer(self, info_hash, doneflag, protocol=protocol_name):
+ """Create a new Server for the torrent.
+
+ @type info_hash: C{string}
+ @param info_hash: the torrent's infohash
+ @type doneflag: C{threading.Event}
+ @param doneflag: the flag to indicate stopping to the new Server
+ @type protocol: C{string}
+ @param protocol: the name to use for the communication protocol
+ (optional, defaults to L{DebTorrent.protocol_name})
+ @rtype: L{SingleRawServer}
+ @return: the new Server that was created
+
+ """
+
new = SingleRawServer(info_hash, self, doneflag, protocol)
self.singlerawservers[info_hash] = new
return new
def shutdown_torrent(self, info_hash):
+ """Shutdown a single torrent's Server.
+
+ @type info_hash: C{string}
+ @param info_hash: the torrent's infohash
+
+ """
+
self.singlerawservers[info_hash]._shutdown()
del self.singlerawservers[info_hash]
def listen_forever(self):
+ """Call the master server's listen loop."""
self.rawserver.listen_forever(self)
for srs in self.singlerawservers.values():
srs.finished = True
@@ -243,4 +588,11 @@
# be wary of name collisions
def external_connection_made(self, ss):
+ """Handle a new incoming connection from the master Server.
+
+ @type ss: unknown
+ @param ss: unknown
+
+ """
+
NewSocketHandler(self, ss)
Modified: debtorrent/branches/http-listen/DebTorrent/__init__.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/__init__.py?rev=96&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/__init__.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/__init__.py Mon Jun 11 07:05:55 2007
@@ -3,7 +3,7 @@
#
# $Id$
-"""The main package to implement the debtorrent protocol.
+"""The main package to implement the DebTorrent protocol.
This package, and it's subpackage L{BT1}, contains all the modules needed
to implement the DebTorrent protocol.
@@ -12,6 +12,10 @@
@var product_name: the name given for the package
@type version_short: C{string}
@var version_short: the short version number
+ at type protocol_name: C{string}
+ at var protocol_name: the protocol name to use in handshaking
+ at type mapbase64: C{string}
+ at var mapbase64: the mapping from 64 bit numbers to string characters
"""
@@ -45,6 +49,7 @@
_idrandom = [None]
def resetPeerIDs():
+ """Reset the generation of peer IDs before generating a new random one."""
try:
f = open('/dev/urandom','rb')
x = f.read(20)
@@ -77,6 +82,17 @@
resetPeerIDs()
def createPeerID(ins = '---'):
+ """Generate a somewhat random peer ID
+
+ @type ins: C{string}
+ @param ins: the length 3 string to insert in the middle of the peer ID
+ between the prefix and the random part of the ID
+ (optional, defaults to '---')
+ @rtype: C{string}
+ @return: the peer ID to use
+
+ """
+
assert type(ins) is StringType
assert len(ins) == 3
return _idprefix + ins + _idrandom[0]
Modified: debtorrent/branches/http-listen/DebTorrent/launchmanycore.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/launchmanycore.py?rev=96&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/launchmanycore.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/launchmanycore.py Mon Jun 11 07:05:55 2007
@@ -1,10 +1,12 @@
#!/usr/bin/env python
-
+#
# Written by John Hoffman
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Manage the downloading of multiple torrents in one process."""
from DebTorrent import PSYCO
if PSYCO.psyco:
@@ -41,6 +43,18 @@
def fmttime(n):
+ """Formats seconds into a human-readable time.
+
+ Formats a given number of seconds into a human-readable time appropriate
+ for display to the user.
+
+ @type n: C{int}
+ @param n: the number of seconds
+ @rtype: C{string}
+ @return: a displayable representation of the number of seconds
+
+ """
+
try:
n = int(n) # n may be None or too large
assert n < 5184000 # 60 days
@@ -51,7 +65,63 @@
return '%d:%02d:%02d' % (h, m, s)
class SingleDownload:
+ """Manage a single torrent download.
+
+ @type controller: L{LaunchMany}
+ @ivar controller: the manager for all torrent downloads
+ @type hash: C{string}
+ @ivar hash: the info hash of the torrent
+ @type response: C{dictionary}
+ @ivar response: the meta info for the torrent
+ @type config: C{dictionary}
+ @ivar config: the configuration parameters
+ @type doneflag: C{threading.Event}
+ @ivar doneflag: the flag that indicates when the torrent is to be shutdown
+ @type waiting: C{boolean}
+ @ivar waiting: unknown
+ @type checking: C{boolean}
+ @ivar checking: unknown
+ @type working: C{boolean}
+ @ivar working: unknown
+ @type seed: C{boolean}
+ @ivar seed: unknown
+ @type closed: C{boolean}
+ @ivar closed: unknown
+ @type status_msg: C{string}
+ @ivar status_msg: the current activity the torrent is engaged in
+ @type status_err: C{list} of C{string}
+ @ivar status_err: the list of errors that have occurred
+ @type status_errtime: C{int}
+ @ivar status_errtime: the time of the last error
+ @type status_done: C{float}
+ @ivar status_done: the fraction of the current activity that is complete
+ @type rawserver: L{ServerPortHandler.SingleRawServer}
+ @ivar rawserver: the simplified Server to use to handle this torrent
+ @type d: L{download_bt1.BT1Download}
+ @ivar d: the downloader for the torrent
+ @type _hashcheckfunc: C{method}
+ @ivar _hashcheckfunc: the method to call to hash check the torrent
+ @type statsfunc: C{method}
+ @ivar statsfunc: the method to call to get the statistics for the running download
+
+ """
+
def __init__(self, controller, hash, response, config, myid):
+ """Initialize the instance and start a new downloader.
+
+ @type controller: L{LaunchMany}
+ @param controller: the manager for all torrent downloads
+ @type hash: C{string}
+ @param hash: the info hash of the torrent
+ @type response: C{dictionary}
+ @param response: the meta info for the torrent
+ @type config: C{dictionary}
+ @param config: the configuration parameters
+ @type myid: C{string}
+ @param myid: the peer ID to use
+
+ """
+
self.controller = controller
self.hash = hash
self.response = response
@@ -77,6 +147,7 @@
self.d = d
def start(self):
+ """Initialize the new torrent download and schedule it for hash checking."""
if not self.d.saveAs(self.saveAs):
self._shutdown()
return
@@ -88,9 +159,31 @@
def saveAs(self, name, length, saveas, isdir):
+ """Determine the location to save the torrent in.
+
+ @type name: C{string}
+ @param name: the name from the torrent's metainfo
+ @type length: C{long}
+ @param length: the total length of the torrent download (not used)
+ @type saveas: C{string}
+ @param saveas: the user specified location to save to
+ @type isdir: C{boolean}
+ @param isdir: whether the torrent needs a directory
+ @rtype: C{string}
+ @return: the location to save the torrent in
+
+ """
+
return self.controller.saveAs(self.hash, name, saveas, isdir)
def hashcheck_start(self, donefunc):
+ """Start the hash checking of the torrent.
+
+ @type donefunc: C{method}
+ @param donefunc: the method to call when the hash checking is complete
+
+ """
+
if self.is_dead():
self._shutdown()
return
@@ -99,6 +192,7 @@
self._hashcheckfunc(donefunc)
def hashcheck_callback(self):
+ """Start the torrent running now that hash checking is complete."""
self.checking = False
if self.is_dead():
self._shutdown()
@@ -112,12 +206,27 @@
self.working = True
def is_dead(self):
+ """Check if the torrent download has been shutdown.
+
+ @rtype: C{boolean}
+ @return: whether the torrent download has been shutdown
+
+ """
+
return self.doneflag.isSet()
def _shutdown(self):
+ """Loudly shutdown the running torrent."""
self.shutdown(False)
def shutdown(self, quiet=True):
+ """Shutdown the running torrent.
+
+ @type quiet: C{boolean}
+ @param quiet: whether to announce the shutdown (optional, defaults to True)
+
+ """
+
if self.closed:
return
self.doneflag.set()
@@ -134,16 +243,36 @@
def display(self, activity = None, fractionDone = None):
- # really only used by StorageWrapper now
+ """Update the current activity's status for later display.
+
+ Really only used by StorageWrapper now.
+
+ @type activity: C{string}
+ @param activity: the activity currently under way
+ (optional, defaults to not changing the current activity)
+ @type fractionDone: C{float}
+ @param fractionDone: the fraction of the activity that is complete
+ (optional, defaults to not changing the current fraction done)
+
+ """
+
if activity:
self.status_msg = activity
if fractionDone is not None:
self.status_done = float(fractionDone)
def finished(self):
+ """Indicate that the download has completed."""
self.seed = True
def error(self, msg):
+ """Add a new error to the list of errors that have occurred.
+
+ @type msg: C{string}
+ @param msg: the error message
+
+ """
+
if self.doneflag.isSet():
self._shutdown()
self.status_err.append(msg)
@@ -151,7 +280,59 @@
class LaunchMany:
+ """Manage the collection of all single torrent downloads.
+
+ @type config: C{dictionary}
+ @ivar config: the configuration parameters
+ @type Output: unknown
+ @ivar Output: the displayer instance to use
+ @type torrent_dir: C{string}
+ @ivar torrent_dir: the directory to parse for torrent files
+ @type torrent_cache: C{dictionary}
+ @ivar torrent_cache: the cache of known torrents, keys are info hashes
+ @type file_cache: C{dictionary}
+ @ivar file_cache: the files found in the parsing of the torrent directory
+ @type blocked_files: C{dictionary}
+ @ivar blocked_files: the torrents in the torrent directory that will not be run
+ @type scan_period: C{int}
+ @ivar scan_period: the number of seconds between scans of L{torrent_dir}
+ @type stats_period: C{int}
+ @ivar stats_period: the number of seconds between printing the stats for the user
+ @type torrent_list: C{list} of C{string}
+ @ivar torrent_list: the list of known torrents' info hashes
+ @type downloads: C{dictionary}
+ @ivar downloads: the currently running downloaders, keys are info hashes
+ @type counter: C{int}
+ @ivar counter: the number of torrents that have been started so far
+ @type doneflag: C{threading.Event}
+ @ivar doneflag: flag to indicate all is to be stopped
+ @type hashcheck_queue: C{list} of C{string}
+ @ivar hashcheck_queue: the list of torrent info hashes waiting to be hash checked
+ @type hashcheck_current: C{string}
+ @ivar hashcheck_current: the info hash of the torrent currently being hash checked
+ @type rawserver: L{RawServer.RawServer}
+ @ivar rawserver: the Server instance to use for the downloads
+ @type listen_port: C{int}
+ @ivar listen_port: the port to listen on for incoming torrent connections
+ @type aptlistener: L{BT1.AptListener.AptListener}
+ @ivar aptlistener: the AptListener instance used to listen for incoming connections from Apt
+ @type ratelimiter: L{RateLimiter.RateLimiter}
+ @ivar ratelimiter: the limiter used to cap the maximum upload rate
+ @type handler: L{ServerPortHandler.MultiHandler}
+ @ivar handler: the multi torrent port listener used to handle connections
+
+ """
+
def __init__(self, config, Output):
+ """Initialize the instance.
+
+ @type config: C{dictionary}
+ @param config: the configuration parameters
+ @type Output: unknown
+ @param Output: the displayer instance to use
+
+ """
+
try:
self.config = config
self.Output = Output
@@ -222,6 +403,7 @@
def scan(self):
+ """Scan the torrent directory for changes."""
self.rawserver.add_task(self.scan, self.scan_period)
r = parsedir(self.torrent_dir, self.torrent_cache,
@@ -238,7 +420,8 @@
self.Output.message('added "'+data['path']+'"')
self.add(hash, data)
- def stats(self):
+ def stats(self):
+ """Call the Output display with the currently running torrents' statistics."""
self.rawserver.add_task(self.stats, self.stats_period)
data = []
for hash in self.torrent_list:
@@ -305,11 +488,27 @@
self.doneflag.set()
def remove(self, hash):
+ """Stop and remove a running torrent.
+
+ @type hash: C{string}
+ @param hash: the info hash of the torrent
+
+ """
+
self.torrent_list.remove(hash)
self.downloads[hash].shutdown()
del self.downloads[hash]
def add(self, hash, data):
+ """Start a new torrent running.
+
+ @type hash: C{string}
+ @param hash: the info hash of the torrent
+ @type data: C{dictionary}
+ @param data: various info about the torrent, including the metainfo
+
+ """
+
c = self.counter
self.counter += 1
x = ''
@@ -324,6 +523,21 @@
def saveAs(self, hash, name, saveas, isdir):
+ """Determine the location to save the torrent in.
+
+ @type hash: C{string}
+ @param hash: the info hash of the torrent
+ @type name: C{string}
+ @param name: the name from the torrent's metainfo
+ @type saveas: C{string}
+ @param saveas: the user specified location to save to
+ @type isdir: C{boolean}
+ @param isdir: whether the torrent needs a directory
+ @rtype: C{string}
+ @return: the location to save the torrent in
+
+ """
+
x = self.torrent_cache[hash]
style = self.config['saveas_style']
if style == 1 or style == 3:
@@ -356,16 +570,26 @@
def hashchecksched(self, hash = None):
+ """Schedule a new torrent for hash checking.
+
+ @type hash: C{string}
+ @param hash: the info hash of the torrent to schedule
+ (optional, default is to start the next torrent in the queue)
+
+ """
+
if hash:
self.hashcheck_queue.append(hash)
if not self.hashcheck_current:
self._hashcheck_start()
def _hashcheck_start(self):
+ """Start hash checking the next torrent in the queue."""
self.hashcheck_current = self.hashcheck_queue.pop(0)
self.downloads[self.hashcheck_current].hashcheck_start(self.hashcheck_callback)
def hashcheck_callback(self):
+ """Start another torrent's hash check now that the current one is complete."""
self.downloads[self.hashcheck_current].hashcheck_callback()
if self.hashcheck_queue:
self._hashcheck_start()
@@ -373,10 +597,24 @@
self.hashcheck_current = None
def died(self, hash):
+ """Inform the Output that the torrent has died.
+
+ @type hash: C{string}
+ @param hash: the info hash of the torrent
+
+ """
+
if self.torrent_cache.has_key(hash):
self.Output.message('DIED: "'+self.torrent_cache[hash]['path']+'"')
def was_stopped(self, hash):
+ """Remove the torrent from the hash check queue, even if it's already happening.
+
+ @type hash: C{string}
+ @param hash: the info hash of the torrent
+
+ """
+
try:
self.hashcheck_queue.remove(hash)
except:
@@ -387,7 +625,21 @@
self._hashcheck_start()
def failed(self, s):
+ """Indicate to the Output that a failure has occurred.
+
+ @type s: C{string}
+ @param s: the failure message
+
+ """
+
self.Output.message('FAILURE: '+s)
def exchandler(self, s):
+ """Indicate to the Output that an exception has occurred.
+
+ @type s: C{string}
+ @param s: the exception that occurred
+
+ """
+
self.Output.exception(s)
More information about the Debtorrent-commits
mailing list