r115 - in /debtorrent/trunk: ./ DebTorrent/ DebTorrent/BT1/
camrdale-guest at users.alioth.debian.org
camrdale-guest at users.alioth.debian.org
Mon Jun 18 05:49:26 UTC 2007
Author: camrdale-guest
Date: Mon Jun 18 05:49:26 2007
New Revision: 115
URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=115
Log:
More documentation.
Modified:
debtorrent/trunk/DebTorrent/BT1/Downloader.py
debtorrent/trunk/DebTorrent/BT1/DownloaderFeedback.py
debtorrent/trunk/DebTorrent/BT1/Filter.py
debtorrent/trunk/DebTorrent/BT1/fakeopen.py
debtorrent/trunk/DebTorrent/RateMeasure.py
debtorrent/trunk/DebTorrent/RawServer.py
debtorrent/trunk/DebTorrent/SocketHandler.py
debtorrent/trunk/DebTorrent/download_bt1.py
debtorrent/trunk/DebTorrent/iprangeparse.py
debtorrent/trunk/DebTorrent/parsedir.py
debtorrent/trunk/DebTorrent/selectpoll.py
debtorrent/trunk/DebTorrent/subnetparse.py
debtorrent/trunk/DebTorrent/torrentlistparse.py
debtorrent/trunk/TODO
Modified: debtorrent/trunk/DebTorrent/BT1/Downloader.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/Downloader.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Downloader.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Downloader.py Mon Jun 18 05:49:26 2007
@@ -822,7 +822,7 @@
# self.picker.seed_seen_recently()
self.disconnectedseeds[id]=clock()
-# def expire_disconnected_seeds(self):
+# def expire_disconnected_seeds(self):
def num_disconnected_seeds(self):
"""Expire old disconnected seeds and calculate the recent number.
Modified: debtorrent/trunk/DebTorrent/BT1/DownloaderFeedback.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/DownloaderFeedback.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/DownloaderFeedback.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/DownloaderFeedback.py Mon Jun 18 05:49:26 2007
@@ -1,8 +1,16 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Gather and display statistics about the download.
+
+ at type INIT_STATE: C{tuple} of C{string}
+ at var INIT_STATE: the state to display, 'L'/'R' = locally/remotely initiated,
+ '+' = encrypted
+
+"""
from cStringIO import StringIO
from urllib import quote
@@ -17,9 +25,81 @@
INIT_STATE = (('R','R+'),('L','L+'))
class DownloaderFeedback:
+ """Gather and display statistics about the download.
+
+ @type choker: L{Choker.Choker}
+ @ivar choker: the Choker instance for the download
+ @type httpdl: L{HTTPDownloder.HTTPDownloader}
+ @ivar httpdl: the HTTP downloader instance used by the download
+ @type add_task: C{method}
+ @ivar add_task: the method to call to schedule future tasks
+ @type upfunc: C{method}
+ @ivar upfunc: the method to call to get the upload rate
+ @type downfunc: C{method}
+ @ivar downfunc: the method to call to get the download rate
+ @type ratemeasure: L{DebTorrent.RateMeasure.RateMeasure}
+ @ivar ratemeasure: the download rate measurement for the download
+ @type leftfunc: C{method}
+ @ivar leftfunc: the method to call to determine the amount of data
+ left to download
+ @type file_length: C{long}
+ @ivar file_length: the total length of the download
+ @type finflag: C{threading.Event}
+ @ivar finflag: flag to indicate when the download is complete
+ @type sp: C{threading.Event}
+ @ivar sp: whether to include the per-connection info in the gathered data
+ @type statistics: L{Statistics.Statistics}
+ @ivar statistics: the statistics gatherer for the download
+ @type lastids: C{list} of C{string}
+ @ivar lastids: the peer IDs that were found the last time
+ @type spewdata: unknown
+ @ivar spewdata: not used
+ @type doneprocessing: C{threading.Event}
+ @ivar doneprocessing: flag that is set when the previous display is complete
+ @type displayfunc: C{method}
+ @ivar displayfunc: the method to call to display the gathered data
+ @type interval: C{int}
+ @ivar interval: the number of seconds between displays of the gathered data
+
+ """
+
def __init__(self, choker, httpdl, add_task, upfunc, downfunc,
ratemeasure, leftfunc, file_length, finflag, sp, statistics,
statusfunc = None, interval = None):
+ """Initialize the instance.
+
+ @type choker: L{Choker.Choker}
+ @param choker: the Choker instance for the download
+ @type httpdl: L{HTTPDownloder.HTTPDownloader}
+ @param httpdl: the HTTP downloader instance used by the download
+ @type add_task: C{method}
+ @param add_task: the method to call to schedule future tasks
+ @type upfunc: C{method}
+ @param upfunc: the method to call to get the upload rate
+ @type downfunc: C{method}
+ @param downfunc: the method to call to get the download rate
+ @type ratemeasure: L{DebTorrent.RateMeasure.RateMeasure}
+ @param ratemeasure: the download rate measurement for the download
+ @type leftfunc: C{method}
+ @param leftfunc: the method to call to determine the amount of data
+ left to download
+ @type file_length: C{long}
+ @param file_length: the total length of the download
+ @type finflag: C{threading.Event}
+ @param finflag: flag to indicate when the download is complete
+ @type sp: C{threading.Event}
+ @param sp: unknown
+ @type statistics: L{Statistics.Statistics}
+ @param statistics: the statistics gatherer for the download
+ @type statusfunc: C{method}
+ @param statusfunc: the method to call to automatically display the gathered data
+ (optional, default is to not automatically display anything)
+ @type interval: C{int}
+ @param interval: the number of seconds between displays of the gathered data
+ (optional, but must be set if the L{statusfunc} is set)
+
+ """
+
self.choker = choker
self.httpdl = httpdl
self.add_task = add_task
@@ -40,6 +120,13 @@
def _rotate(self):
+ """Rotate the list of connections so it starts roughly where it did before.
+
+ @rtype: C{list} of L{Connecter.Connection}
+ @return: the connections from peers to the client
+
+ """
+
cs = self.choker.connections
for id in self.lastids:
for i in xrange(len(cs)):
@@ -48,6 +135,13 @@
return cs
def spews(self):
+ """Get information about the currently connected peers.
+
+ @rtype: C{list} of C{dictionary}
+ @return: information about all the current peer connections
+
+ """
+
l = []
cs = self._rotate()
self.lastids = [c.get_id() for c in cs]
@@ -101,6 +195,15 @@
def gather(self, displayfunc = None):
+ """Gather the information about the download.
+
+ @type displayfunc: unknown
+ @param displayfunc: not used (optional)
+ @rtype: C{dictionary}
+ @return: various information about the download
+
+ """
+
s = {'stats': self.statistics.update()}
if self.sp.isSet():
s['spew'] = self.spews()
@@ -126,6 +229,13 @@
def display(self, displayfunc):
+ """Gather the information about the download and display it.
+
+ @type displayfunc: C{method}
+ @param displayfunc: the method to call with the gathered information
+
+ """
+
if not self.doneprocessing.isSet():
return
self.doneprocessing.clear()
@@ -148,10 +258,20 @@
def autodisplay(self, displayfunc, interval):
+ """Initialize the automatic displayer.
+
+ @type displayfunc: C{method}
+ @param displayfunc: the method to call to display the gathered data
+ @type interval: C{int}
+ @param interval: the number of seconds between displays of the gathered data
+
+ """
+
self.displayfunc = displayfunc
self.interval = interval
self._autodisplay()
def _autodisplay(self):
+ """Call the displayer and reschedule."""
self.add_task(self._autodisplay, self.interval)
self.display(self.displayfunc)
Modified: debtorrent/trunk/DebTorrent/BT1/Filter.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/Filter.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/Filter.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/Filter.py Mon Jun 18 05:49:26 2007
@@ -1,15 +1,54 @@
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+"""Does nothing."""
+
class Filter:
+ """Does nothing.
+
+ @type callback: unknown
+ @ivar callback: unknown
+
+ """
+
def __init__(self, callback):
+ """Initialize the instance.
+
+ @type callback: unknown
+ @param callback: unknown
+
+ """
+
self.callback = callback
def check(self, ip, paramslist, headers):
+ """Returns None.
+
+ @type ip: unknown
+ @param ip: unknown
+ @type paramslist: unknown
+ @param paramslist: unknown
+ @type headers: unknown
+ @param headers: unknown
+ @rtype: unknown
+ @return: None
+
+ """
def params(key, default = None, l = paramslist):
+ """Unknown
+
+ @type key: unknown
+ @param key: unknown
+ @type default: unknown
+ @param default: unknown
+ @type l: unknown
+ @param l: unknown
+
+ """
+
if l.has_key(key):
return l[key][0]
return default
Modified: debtorrent/trunk/DebTorrent/BT1/fakeopen.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/fakeopen.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/fakeopen.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/fakeopen.py Mon Jun 18 05:49:26 2007
@@ -1,27 +1,66 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
+#
+# $Id$
-# $Id$
+"""Fake file handling objects (not used)."""
from string import join
class FakeHandle:
+ """Fake file handles stored in memory.
+
+ @type name: C{string}
+ @ivar name: the file name
+ @type fakeopen: L{FakeOpen}
+ @ivar fakeopen: the collection of all fake files
+ @type pos: C{int}
+ @ivar pos: the current position within the file
+
+ """
+
def __init__(self, name, fakeopen):
+ """Initialize the instance.
+
+ @type name: C{string}
+ @param name: the file name
+ @type fakeopen: L{FakeOpen}
+ @param fakeopen: the collection of all fake files
+
+ """
+
self.name = name
self.fakeopen = fakeopen
self.pos = 0
def flush(self):
+ """Does nothing."""
pass
def close(self):
+ """Does nothing."""
pass
def seek(self, pos):
+ """Seek to a position within the file.
+
+ @type pos: C{int}
+ @param pos: the position to seek to
+
+ """
self.pos = pos
def read(self, amount = None):
+ """Read data from the file.
+
+ @type amount: C{int}
+ @param amount: the amount of data to read
+ @rtype: C{string}
+ @return: the read data
+
+ """
+
old = self.pos
f = self.fakeopen.files[self.name]
if self.pos >= len(f):
@@ -34,6 +73,13 @@
return join(f[old:self.pos], '')
def write(self, s):
+ """Write data to the file.
+
+ @type s: C{string}
+ @param s: the data to write
+
+ """
+
f = self.fakeopen.files[self.name]
while len(f) < self.pos:
f.append(chr(0))
@@ -41,23 +87,66 @@
self.pos += len(s)
class FakeOpen:
+ """Fake file objects.
+
+ @type files: C{dictionary} of {C{string}: C{list} of C{char}}
+ @ivar files: the stored file data, keys are file names, values are lists
+ of characters that are the data
+
+ """
+
def __init__(self, initial = {}):
+ """Initialize the instance.
+
+ @type initial: C{dictionary}
+ @param initial: the initial files to create
+
+ """
+
self.files = {}
for key, value in initial.items():
self.files[key] = list(value)
def open(self, filename, mode):
- """currently treats everything as rw - doesn't support append"""
+ """Open a new or existing file.
+
+ Treats everything as read/write, doesn't support append.
+
+ @type filename: C{string}
+ @param filename: the file name to open
+ @type mode: C{string}
+ @param mode: the file mode to open (not used)
+
+ """
self.files.setdefault(filename, [])
return FakeHandle(filename, self)
def exists(self, file):
+ """Check if the file exists.
+
+ @type file: C{string}
+ @param file: the file name to check
+ @rtype: C{boolean}
+ @return: whether the file exists
+
+ """
+
return self.files.has_key(file)
def getsize(self, file):
+ """Get the current size of the file.
+
+ @type file: C{string}
+ @param file: the file name to check
+ @rtype: C{int}
+ @return: the file size
+
+ """
+
return len(self.files[file])
def test_normal():
+ """Test the fake file handling objects."""
f = FakeOpen({'f1': 'abcde'})
assert f.exists('f1')
assert not f.exists('f2')
Modified: debtorrent/trunk/DebTorrent/RateMeasure.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/RateMeasure.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/RateMeasure.py (original)
+++ debtorrent/trunk/DebTorrent/RateMeasure.py Mon Jun 18 05:49:26 2007
@@ -1,8 +1,15 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
+#
+# $Id$
-# $Id$
+"""Simple measurement of the download rate.
+
+ at type FACTOR: C{float}
+ at var FACTOR: the factor to use to adjust for TCP overhead
+
+"""
from clock import clock
try:
@@ -14,7 +21,31 @@
FACTOR = 0.999
class RateMeasure:
+ """Simple measurement of the download rate.
+
+ @type last: C{float}
+ @ivar last: the last time the rate was updated
+ @type time: C{float}
+ @ivar time: the amount of time that has elapsed since data first came in
+ @type got: C{float}
+ @ivar got: the amount of data that's come in so far
+ @type remaining: C{float}
+ @ivar remaining: the number of seconds remaining from the last calculation
+ @type broke: C{boolean}
+ @ivar broke: unused
+ @type got_anything: C{boolean}
+ @ivar got_anything: whether any data has been received yet
+ @type last_checked: C{float}
+ @ivar last_checked: the last time the finishing time was calculated
+ @type rate: C{int}
+ @ivar rate: the last calculated download rate
+ @type lastten: C{boolean}
+ @ivar lastten: whether the download is in the last ten seconds
+
+ """
+
def __init__(self):
+ """Initialize the instance."""
self.last = None
self.time = 1.0
self.got = 0.0
@@ -26,6 +57,13 @@
self.lastten = False
def data_came_in(self, amount):
+ """Add new data received to the rate.
+
+ @type amount: C{int}
+ @param amount: the amount of data received
+
+ """
+
if not self.got_anything:
self.got_anything = True
self.last = clock()
@@ -33,9 +71,26 @@
self.update(amount)
def data_rejected(self, amount):
+ """Add new data received to the rate.
+
+ @type amount: C{int}
+ @param amount: the amount of data received
+
+ """
+
pass
def get_time_left(self, left):
+ """Calculate the amount of time left to complete the download.
+
+ @type left: C{long}
+ @param left: the amount of data still to be downloaded
+ @rtype: C{float}
+ @return: the number of seconds left in the download
+ (or None if no data has been received)
+
+ """
+
t = clock()
if not self.got_anything:
return None
@@ -62,6 +117,13 @@
return self.remaining
def update(self, amount):
+ """Update the rate with new received data.
+
+ @type amount: C{int}
+ @param amount: the amount of data received
+
+ """
+
t = clock()
t1 = int(t)
l1 = int(self.last)
Modified: debtorrent/trunk/DebTorrent/RawServer.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/RawServer.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/RawServer.py (original)
+++ debtorrent/trunk/DebTorrent/RawServer.py Mon Jun 18 05:49:26 2007
@@ -1,8 +1,15 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Manage connections to and tasks to be run on the server.
+
+ at type READSIZE: C{int}
+ at var READSIZE: the maximum amount of data to read from any sockets
+
+"""
from bisect import insort
from SocketHandler import SocketHandler, UPnP_ERROR
@@ -22,6 +29,7 @@
def autodetect_ipv6():
+ """Detect whether IPv6 connections are supported (not used)."""
try:
assert sys.version_info >= (2,3)
assert socket.has_ipv6
@@ -31,24 +39,96 @@
return 1
def autodetect_socket_style():
- if sys.platform.find('linux') < 0:
- return 1
- else:
- try:
- f = open('/proc/sys/net/ipv6/bindv6only','r')
- dual_socket_style = int(f.read())
- f.close()
- return int(not dual_socket_style)
- except:
- return 0
+ """Determine if an IPv6 server socket will also field IPv4 connections."""
+ if sys.platform.find('linux') < 0:
+ return 1
+ else:
+ try:
+ f = open('/proc/sys/net/ipv6/bindv6only','r')
+ dual_socket_style = int(f.read())
+ f.close()
+ return int(not dual_socket_style)
+ except:
+ return 0
READSIZE = 32768
class RawServer:
+ """Manage connections and tasks like a server.
+
+ Mostly just manages the tasks, any socket related duties are passed on to
+ the L{SocketHandler.SocketHandler} instance.
+
+ @type timeout_check_interval: unknown
+ @ivar timeout_check_interval: unknown
+ @type timeout: unknown
+ @ivar timeout: unknown
+ @type servers: unknown
+ @ivar servers: unknown
+ @type single_sockets: unknown
+ @ivar single_sockets: unknown
+ @type dead_from_write: unknown
+ @ivar dead_from_write: unknown
+ @type doneflag: C{threading.Event}
+ @ivar doneflag: flag to indicate the program is to be shutdown
+ @type noisy: C{boolean}
+ @ivar noisy: whether to report exceptions in running the task
+ @type failfunc: C{method}
+ @ivar failfunc: method to call to report failures
+ @type errorfunc: C{method}
+ @ivar errorfunc: method to call to report exceptions
+ @type exccount: unknown
+ @ivar exccount: unknown
+ @type funcs: unknown
+ @ivar funcs: unknown
+ @type externally_added: unknown
+ @ivar externally_added: unknown
+ @type finished: unknown
+ @ivar finished: unknown
+ @type tasks_to_kill: unknown
+ @ivar tasks_to_kill: unknown
+ @type excflag: C{threading.Event}
+ @ivar excflag: the flag to use to indicate an exception has occurred
+ @type sockethandler: L{SocketHandler.SocketHandler}
+ @ivar sockethandler: the handler to use to manage all open sockets
+
+ """
+
def __init__(self, doneflag, timeout_check_interval, timeout, noisy = True,
ipv6_enable = True, failfunc = lambda x: None, errorfunc = None,
sockethandler = None, excflag = Event()):
+ """Initialize the instance and start the socket handler.
+
+ @type doneflag: C{threading.Event}
+ @param doneflag: flag to indicate the program is to be shutdown
+ @type timeout_check_interval: C{float}
+ @param timeout_check_interval: seconds to wait between checking if any
+ connections have timed out
+ @type timeout: C{float}
+ @param timeout: seconds to wait between closing sockets on which
+ nothing has been received on
+ @type noisy: C{boolean}
+ @param noisy: whether to report exceptions in running the task
+ (optional, defaults to True)
+ @type ipv6_enable: C{boolean}
+ @param ipv6_enable: allow the client to connect to peers via IPv6
+ (optional, defaults to True)
+ @type failfunc: C{method}
+ @param failfunc: method to call to report failures
+ (optional, defaults to not reporting)
+ @type errorfunc: C{method}
+ @param errorfunc: method to call to report exceptions
+ (optional, defaults to not reporting)
+ @type sockethandler: L{SocketHandler.SocketHandler}
+ @param sockethandler: the handler to use to manage all open sockets
+ (optional, defaults to creating a new one)
+ @type excflag: C{threading.Event}
+ @param excflag: the flag to use to indicate an exception has occurred
+ (optional, defaults to using a new flag)
+
+ """
+
self.timeout_check_interval = timeout_check_interval
self.timeout = timeout
self.servers = {}
@@ -71,45 +151,165 @@
self.add_task(self.scan_for_timeouts, timeout_check_interval)
def get_exception_flag(self):
+ """Get the flag used to indicate exceptions.
+
+ @rtype: C{threading.Event}
+ @return: the exception flag
+
+ """
+
return self.excflag
def _add_task(self, func, delay, id = None):
+ """Add the task to the sorted list of tasks to execute.
+
+ @type func: C{method}
+ @param func: the task to be run
+ @type delay: C{int}
+ @param delay: the number of seconds to delay before running the task
+ @type id: unknown
+ @param id: an identifier to later find the task by
+
+ """
+
assert float(delay) >= 0
insort(self.funcs, (clock() + delay, func, id))
def add_task(self, func, delay = 0, id = None):
+ """Add the task to the list of tasks to schedule.
+
+ @type func: C{method}
+ @param func: the task to be run
+ @type delay: C{int}
+ @param delay: the number of seconds to delay before running the task
+ @type id: unknown
+ @param id: an identifier to later find the task by
+
+ """
+
assert float(delay) >= 0
self.externally_added.append((func, delay, id))
def scan_for_timeouts(self):
+ """Scan the open sockets for any timeouts."""
self.add_task(self.scan_for_timeouts, self.timeout_check_interval)
self.sockethandler.scan_for_timeouts()
def bind(self, port, bind = '', reuse = False,
ipv6_socket_style = 1, upnp = False):
+ """Bind to listen on a single port.
+
+ @type port: C{int}
+ @param port: the port to listen on
+ @type bind: C{string}
+ @param bind: the IP address to bind to (optional, defaults to all)
+ @type reuse: C{boolean}
+ @param reuse: whether to use SO_REUSEADDR to bind (optional, defaults
+ to False). This allows the bind to work if the socket is still
+ open in the TIME_WAIT state from a recently shutdown server.
+ @type ipv6_socket_style: C{int}
+ @param ipv6_socket_style: whether an IPv6 server socket will also
+ field IPv4 connections (optional, defaults to yes)
+ @type upnp: C{boolean}
+ @param upnp: whether to attempt to use UPnP to open the port in a
+ firewall (optional, defaults to False)
+
+ """
+
self.sockethandler.bind(port, bind, reuse, ipv6_socket_style, upnp)
def find_and_bind(self, minport, maxport, bind = '', reuse = False,
ipv6_socket_style = 1, upnp = 0, randomizer = False):
+ """Bind to listen on a single port within a range.
+
+ @type minport: C{int}
+ @param minport: the minimum port to listen on
+ @type maxport: C{int}
+ @param maxport: the maximum port to listen on
+ @type bind: C{string}
+ @param bind: the addresses to bind to (optional, defaults to the
+ default IPv6 and IPv4 addreses). Parsed as a comma seperated
+ list (can be IP addresses or hostnames).
+ @type reuse: C{boolean}
+ @param reuse: whether to use SO_REUSEADDR to bind (optional, defaults
+ to False). This allows the bind to work if the socket is still
+ open in the TIME_WAIT state from a recently shutdown server.
+ @type ipv6_socket_style: C{int}
+ @param ipv6_socket_style: whether an IPv6 server socket will also
+ field IPv4 connections (optional, defaults to yes)
+ @type upnp: C{boolean}
+ @param upnp: whether to attempt to use UPnP to open the port in a
+ firewall (optional, defaults to False)
+ @type randomizer: C{boolean}
+ @param randomizer: whether to randomize the range or use it sequentially
+ @rtype: C{int}
+ @return: the port that was bound to
+
+ """
+
return self.sockethandler.find_and_bind(minport, maxport, bind, reuse,
ipv6_socket_style, upnp, randomizer)
def start_connection_raw(self, dns, socktype, handler = None):
+ """Initiate a new connection to a peer (setting the type of socket).
+
+ @type dns: (C{string}, C{int})
+ @param dns: the IP address and port number to contact the peer on
+ @type socktype: C{int}
+ @param socktype: the type of socket to open
+ @type handler: unknown
+ @param handler: the data handler to use to process data on the connection
+ (optional, defaults to using the defualt handler)
+ @rtype: L{SocketHandler.SingleSocket}
+ @return: the new connection made to the peer
+
+ """
+
return self.sockethandler.start_connection_raw(dns, socktype, handler)
def start_connection(self, dns, handler = None, randomize = False):
+ """Initiate 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 using the defualt handler)
+ @type randomize: C{boolean}
+ @param randomize: whether to randomize the possible sockets or
+ choose one sequentially
+ @rtype: L{SocketHandler.SingleSocket}
+ @return: the new connection made to the peer
+
+ """
+
return self.sockethandler.start_connection(dns, handler, randomize)
def get_stats(self):
+ """Get some information about the bound interfaces and ports.
+
+ @rtype: C{dictionary}
+ @return: info about the bound interfaces
+
+ """
+
return self.sockethandler.get_stats()
def pop_external(self):
+ """Add the waiting externally added tasks to the list of tasks to process."""
while self.externally_added:
(a, b, c) = self.externally_added.pop(0)
self._add_task(a, b, c)
def listen_forever(self, handler):
+ """Start the server listening on sockets and processing tasks.
+
+ @type handler: unknown
+ @param handler: the default data handler to use to process data on connections
+
+ """
+
self.sockethandler.set_handler(handler)
try:
while not self.doneflag.isSet():
@@ -164,12 +364,21 @@
self.finished.set()
def is_finished(self):
+ """Check if the server is done listening.
+
+ @rtype: C{boolean}
+ @return: whether the server is done listeneing
+
+ """
+
return self.finished.isSet()
def wait_until_finished(self):
+ """Wait until the server is done listeneing."""
self.finished.wait()
def _kill_tasks(self):
+ """Remove the pending list of tasks to remove from those tasks still pending."""
if self.tasks_to_kill:
new_funcs = []
for (t, func, id) in self.funcs:
@@ -179,9 +388,24 @@
self.tasks_to_kill = []
def kill_tasks(self, id):
+ """Remove tasks from the list of those pending to execute.
+
+ @type id: unknown
+ @param id: an identifier find the tasks by
+
+ """
+
self.tasks_to_kill.append(id)
def exception(self, kbint = False):
+ """Print an exception that has occurred.
+
+ @type kbint: C{boolean}
+ @param kbint: whether the exception was from a KeyboardInterrupt
+ (optional, defaults to False)
+
+ """
+
if not kbint:
self.excflag.set()
self.exccount += 1
@@ -195,7 +419,18 @@
self.errorfunc(data.getvalue())
def set_handler(self, handler, port = None):
+ """Set the handler to use for a port (or the default handler).
+
+ @type handler: unknown
+ @param handler: the data handler to use to process data on the port
+ @type port: C{int}
+ @param port: the port to use the handler for
+ (optional, defaults to setting the default handler)
+
+ """
+
self.sockethandler.set_handler(handler, port)
def shutdown(self):
+ """Shutdown the socket handler."""
self.sockethandler.shutdown()
Modified: debtorrent/trunk/DebTorrent/SocketHandler.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/SocketHandler.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/SocketHandler.py (original)
+++ debtorrent/trunk/DebTorrent/SocketHandler.py Mon Jun 18 05:49:26 2007
@@ -1,8 +1,17 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Handle all sockets.
+
+ at type all: C{int}
+ at var all: all events to check for, both input and output
+ at type UPnP_ERROR: C{string}
+ at var UPnP_ERROR: the error string used for UPnP errors
+
+"""
import socket
from errno import EWOULDBLOCK, ECONNREFUSED, EHOSTUNREACH
@@ -30,7 +39,44 @@
UPnP_ERROR = "unable to forward port via UPnP"
class SingleSocket:
+ """Manage a single socket.
+
+ @type socket_handler: L{SocketHandler}
+ @ivar socket_handler: the collection of all sockets
+ @type socket: C{socket.socket}
+ @ivar socket: the socket to manage
+ @type handler: unknown
+ @ivar handler: the handler to use for all communications on the socket
+ @type buffer: C{list} of C{string}
+ @ivar buffer: the list of data waiting to be written on the socket
+ @type last_hit: C{float}
+ @ivar last_hit: the last time data was received on the socket
+ @type fileno: C{int}
+ @ivar fileno: the file number of the socket
+ @type connected: C{boolean}
+ @ivar connected: whether this socket has received an event yet
+ @type skipped: C{int}
+ @ivar skipped: the number of consecutive writes to the socket that have failed
+ @type ip: C{string}
+ @ivar ip: the IP address to use if one can't be obtained from the socket
+
+ """
+
def __init__(self, socket_handler, sock, handler, ip = None):
+ """
+
+ @type socket_handler: L{SocketHandler}
+ @param socket_handler: the collection of all sockets
+ @type sock: C{socket.socket}
+ @param sock: the socket to manage
+ @type handler: unknown
+ @param handler: the handler to use for all communications on the socket
+ @type ip: C{string}
+ @param ip: the IP address to use if one can't be obtained from the socket
+ (optional, defaults to 'unknown')
+
+ """
+
self.socket_handler = socket_handler
self.socket = sock
self.handler = handler
@@ -49,6 +95,17 @@
self.ip = ip
def get_ip(self, real=False):
+ """Get the IP address of the socket.
+
+ @type real: C{boolean}
+ @param real: whether to try and get the IP address directly from the
+ socket or trust the one supplied when the instance was created
+ (optional, defaults to False)
+ @rtype: C{string}
+ @return: the IP address
+
+ """
+
if real:
try:
self.ip = self.socket.getpeername()[0]
@@ -57,16 +114,7 @@
return self.ip
def close(self):
- '''
- for x in xrange(5,0,-1):
- try:
- f = inspect.currentframe(x).f_code
- print (f.co_filename,f.co_firstlineno,f.co_name)
- del f
- except:
- pass
- print ''
- '''
+ """Close the socket."""
assert self.socket
self.connected = False
sock = self.socket
@@ -77,12 +125,37 @@
sock.close()
def shutdown(self, val):
+ """Shutdown the socket.
+
+ @type val: C{int}
+ @param val: the type of event to shutdown the socket for.
+ 0 = reading, 1 = writing, 2 = reading and writing.
+
+ """
+
self.socket.shutdown(val)
def is_flushed(self):
+ """Check if the socket is flushed (no data is waiting to be sent).
+
+ @rtype: C{boolean}
+ @return: whether the socket is flushed
+
+ """
+
return not self.buffer
def write(self, s):
+ """Write data out on the socket.
+
+ Adds the data to the buffer of data waiting to be written, then tries
+ to write the waiting data out.
+
+ @type s: C{string}
+ @param s: the data to write
+
+ """
+
# self.check.write(s)
assert self.socket is not None
self.buffer.append(s)
@@ -90,6 +163,13 @@
self.try_write()
def try_write(self):
+ """Try to write waiting data on the socket.
+
+ Will try to write all buffered data on the socket. If a send fails,
+ the attempt will stop. 3 consecutive failed attempts will cause the
+ socket to be declared dead.
+
+ """
if self.connected:
dead = False
try:
@@ -121,10 +201,62 @@
self.socket_handler.poll.register(self.socket, POLLIN)
def set_handler(self, handler):
+ """Set the handler to use for this socket.
+
+ @type handler: unknown
+ @param handler: the handler to use for all communications on the socket
+
+ """
+
self.handler = handler
class SocketHandler:
+ """The collection of all open sockets.
+
+ @type timeout: C{float}
+ @ivar timeout: seconds to wait between closing sockets on which
+ nothing has been received on
+ @type ipv6_enable: C{boolean}
+ @ivar ipv6_enable: allow the client to connect to peers via IPv6
+ @type readsize: C{int}
+ @ivar readsize: the maximum amount of data to read from a socket
+ @type poll: C{select.poll}
+ @ivar poll: the poll object to use to poll the sockets
+ @type single_sockets: C{dictionary} of {C{int}: L{SingleSocket}}
+ @ivar single_sockets: the collection of all open sockets, keys are the
+ socket's file number
+ @type dead_from_write: C{list} of L{SingleSocket}
+ @ivar dead_from_write: the sockets that have failed due to writing
+ @type max_connects: C{int}
+ @ivar max_connects: the maximum number of sockets to have open at atime
+ @type port_forwarded: C{int}
+ @ivar port_forwarded: the port that was forwarded by UPnP
+ @type servers: C{dictionary} of {C{int}: C{socket.socket}}
+ @ivar servers: the socket listeners, keys are the file numbers
+ @type interfaces: C{list} of C{string}
+ @ivar interfaces: the interfaces that have been bound to
+ @type ports: C{list} of C{int}
+ @ivar ports: the ports that are being listened on
+ @type handlers: C{dictionary} of {C{int}: unknown}
+ @ivar handlers: the handlers that are used for the listened ports,
+ keys are the ports
+
+ """
+
def __init__(self, timeout, ipv6_enable, readsize = 100000):
+ """Initialize the instance.
+
+ @type timeout: C{float}
+ @param timeout: seconds to wait between closing sockets on which
+ nothing has been received on
+ @type ipv6_enable: C{boolean}
+ @param ipv6_enable: allow the client to connect to peers via IPv6
+ @type readsize: C{int}
+ @param readsize: the maximum amount of data to read from a socket
+ (optional, defaults to 100000)
+
+ """
+
self.timeout = timeout
self.ipv6_enable = ipv6_enable
self.readsize = readsize
@@ -140,6 +272,7 @@
self.handlers = {}
def scan_for_timeouts(self):
+ """Check the sockets for timeouts."""
t = clock() - self.timeout
tokill = []
for s in self.single_sockets.values():
@@ -150,6 +283,27 @@
self._close_socket(k)
def bind(self, port, bind = '', reuse = False, ipv6_socket_style = 1, upnp = 0):
+ """Bind to listen on a single port.
+
+ @type port: C{int}
+ @param port: the port to listen on
+ @type bind: C{string}
+ @param bind: the IP address to bind to (optional, defaults to all)
+ @type reuse: C{boolean}
+ @param reuse: whether to use SO_REUSEADDR to bind (optional, defaults
+ to False). This allows the bind to work if the socket is still
+ open in the TIME_WAIT state from a recently shutdown server.
+ @type ipv6_socket_style: C{int}
+ @param ipv6_socket_style: whether an IPv6 server socket will also
+ field IPv4 connections (optional, defaults to yes)
+ @type upnp: C{boolean}
+ @param upnp: whether to attempt to use UPnP to open the port in a
+ firewall (optional, defaults to False)
+ @raise socket.error: if the port can not be bound or UPnP fails
+
+ """
+
+
port = int(port)
addrinfos = []
# Don't reinitialize to allow multiple binds
@@ -218,6 +372,34 @@
def find_and_bind(self, minport, maxport, bind = '', reuse = False,
ipv6_socket_style = 1, upnp = 0, randomizer = False):
+ """Bind to listen on a single port within a range.
+
+ @type minport: C{int}
+ @param minport: the minimum port to listen on
+ @type maxport: C{int}
+ @param maxport: the maximum port to listen on
+ @type bind: C{string}
+ @param bind: the addresses to bind to (optional, defaults to the
+ default IPv6 and IPv4 addreses). Parsed as a comma seperated
+ list (can be IP addresses or hostnames).
+ @type reuse: C{boolean}
+ @param reuse: whether to use SO_REUSEADDR to bind (optional, defaults
+ to False). This allows the bind to work if the socket is still
+ open in the TIME_WAIT state from a recently shutdown server.
+ @type ipv6_socket_style: C{int}
+ @param ipv6_socket_style: whether an IPv6 server socket will also
+ field IPv4 connections (optional, defaults to yes)
+ @type upnp: C{boolean}
+ @param upnp: whether to attempt to use UPnP to open the port in a
+ firewall (optional, defaults to False)
+ @type randomizer: C{boolean}
+ @param randomizer: whether to randomize the range or use it sequentially
+ @rtype: C{int}
+ @return: the port that was bound to
+ @raise socket.error: if none of the ports in the range can be bound
+
+ """
+
e = 'maxport less than minport - no ports to check'
if maxport-minport < 50 or not randomizer:
portrange = range(minport, maxport+1)
@@ -241,6 +423,16 @@
def set_handler(self, handler, port = None):
+ """Set the handler to use for a port (or the default handler).
+
+ @type handler: unknown
+ @param handler: the data handler to use to process data on the port
+ @type port: C{int}
+ @param port: the port to use the handler for
+ (optional, defaults to setting the default handler)
+
+ """
+
if port is None:
self.handler = handler
else:
@@ -248,6 +440,21 @@
def start_connection_raw(self, dns, socktype = socket.AF_INET, handler = None):
+ """Initiate a new connection to a peer (setting the type of socket).
+
+ @type dns: (C{string}, C{int})
+ @param dns: the IP address and port number to contact the peer on
+ @type socktype: C{int}
+ @param socktype: the type of socket to open
+ @type handler: unknown
+ @param handler: the data handler to use to process data on the connection
+ (optional, defaults to using the defualt handler)
+ @rtype: L{SocketHandler.SingleSocket}
+ @return: the new connection made to the peer
+ @raise socket.error: if the connection fails
+
+ """
+
if handler is None:
handler = self.handler
sock = socket.socket(socktype, socket.SOCK_STREAM)
@@ -265,6 +472,22 @@
def start_connection(self, dns, handler = None, randomize = False):
+ """Initiate 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 using the defualt handler)
+ @type randomize: C{boolean}
+ @param randomize: whether to randomize the possible sockets or
+ choose one sequentially
+ @rtype: L{SocketHandler.SingleSocket}
+ @return: the new connection made to the peer
+ @raise socket.error: if the connection fails
+
+ """
+
if handler is None:
handler = self.handler
if sys.version_info < (2,2):
@@ -295,9 +518,17 @@
def _sleep(self):
+ """Sleep for one second."""
sleep(1)
def handle_events(self, events):
+ """Handle any events that have occurred on the open sockets.
+
+ @type events: C{list} of (C{int}, C{int})
+ @param events: the socket file descriptors and event types that have occurred on them
+
+ """
+
for sock, event in events:
s = self.servers.get(sock)
if s:
@@ -345,6 +576,7 @@
s.handler.connection_flushed(s)
def close_dead(self):
+ """Close sockets that have failed to be written."""
while self.dead_from_write:
old = self.dead_from_write
self.dead_from_write = []
@@ -353,10 +585,27 @@
self._close_socket(s)
def _close_socket(self, s):
+ """Close an open socket.
+
+ @type s: L{SingleSocket}
+ @param s: the socket to close
+
+ """
+
s.close()
s.handler.connection_lost(s)
def do_poll(self, t):
+ """Poll the open sockets.
+
+ If the poll returns None (which it shouldn't), then 5% of the open
+ sockets will be closed.
+
+ @type t: C{float}
+ @param t: seconds to wait for an event before timing out
+
+ """
+
r = self.poll.poll(t*timemult)
if r is None:
connects = len(self.single_sockets)
@@ -371,12 +620,20 @@
return r
def get_stats(self):
+ """Get some information about the bound interfaces and ports.
+
+ @rtype: C{dictionary}
+ @return: info about the bound interfaces
+
+ """
+
return { 'interfaces': self.interfaces,
'port': self.ports,
'upnp': self.port_forwarded is not None }
def shutdown(self):
+ """Close all open sockets and servers."""
for ss in self.single_sockets.values():
try:
ss.close()
Modified: debtorrent/trunk/DebTorrent/download_bt1.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/download_bt1.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/download_bt1.py (original)
+++ debtorrent/trunk/DebTorrent/download_bt1.py Mon Jun 18 05:49:26 2007
@@ -115,7 +115,7 @@
('ipv6_enabled', 0,
'allow the client to connect to peers via IPv6'),
('ipv6_binds_v4', autodetect_socket_style(),
- "set if an IPv6 server socket won't also field IPv4 connections"),
+ "set if an IPv6 server socket will also field IPv4 connections"),
('upnp_nat_access', 1,
'attempt to autoconfigure a UPnP router to forward a server port ' +
'(0 = disabled, 1 = mode 1 [fast], 2 = mode 2 [slow])'),
Modified: debtorrent/trunk/DebTorrent/iprangeparse.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/iprangeparse.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/iprangeparse.py (original)
+++ debtorrent/trunk/DebTorrent/iprangeparse.py Mon Jun 18 05:49:26 2007
@@ -167,7 +167,7 @@
@type ip_beg: C{string}
@param ip_beg: the beginning IP address for the range
@type ip_end: C{string}
- @param ip_end: the neding Ip address for the range
+ @param ip_end: the ending IP address for the range
(optional, defaults to the beginning IP address)
"""
@@ -311,7 +311,7 @@
The file to parse must have lines in the format 'whatever:whatever:ip-ip'.
The 'whatever' will be ignored, and the IP addresses must be in IPv4
- format. If the rang is only a single IP address, omit the '-' and the
+ format. If the range is only a single IP address, omit the '-' and the
second 'ip'. Empty lines will be ignored, as will lines beginning with
'#' which can be used for comments.
Modified: debtorrent/trunk/DebTorrent/parsedir.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/parsedir.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/parsedir.py (original)
+++ debtorrent/trunk/DebTorrent/parsedir.py Mon Jun 18 05:49:26 2007
@@ -43,7 +43,7 @@
@type directory: C{string}
@param directory: the directory to parse (somewhat recursively)
@type parsed: C{dictionary}
- @param parsed: the torrent files that were previously found
+ @param parsed: the cache of all torrent files that were ever found
@type files: C{dictionary}
@param files: the files that were previously found
@type blocked: C{dictionary}
@@ -58,8 +58,9 @@
@param errfunc: the method to call to print status/warning/error messages
(optional, defaults to the L{_errfunc} function)
@rtype: (C{dictionary}, C{dictionary}, C{dictionary}, C{dictionary}, C{dictionary})
- @return: all the torrents found, all the files found, all the files blocked,
- the new torrents that were found, the torrents that are now missing
+ @return: the cache of all torrents ever found, all the files found, all
+ the files blocked, the new torrents that were found, the torrents
+ that are now missing
"""
Modified: debtorrent/trunk/DebTorrent/selectpoll.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/selectpoll.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/selectpoll.py (original)
+++ debtorrent/trunk/DebTorrent/selectpoll.py Mon Jun 18 05:49:26 2007
@@ -1,8 +1,25 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
+#
+# $Id$
-# $Id$
+"""Provides socket polling for operating systems that don't support it.
+
+This class is similar to the C{select} module in the standard library, and
+provides the needed functionality for operating systems that don't support
+it in the C{select} module.
+
+ at type POLLIN: C{int}
+ at var POLLIN: There is data to read
+ at type POLLOUT: C{int}
+ at var POLLOUT: Ready for output: writing will not block
+ at type POLLERR: C{int}
+ at var POLLERR: Error condition of some sort
+ at type POLLHUP: C{int}
+ at var POLLHUP: Hung up
+
+"""
from select import select, error
from time import sleep
@@ -14,11 +31,30 @@
POLLHUP = 16
class poll:
+ """Polling object to poll open sockets for events.
+
+ @type rlist: C{list} of C{int}
+ @ivar rlist: the socket file descriptors waiting for data
+ @type wlist: C{list} of C{int}
+ @ivar wlist: the socket file descriptors waiting to send data
+
+ """
+
def __init__(self):
+ """Initialize the instance."""
self.rlist = []
self.wlist = []
def register(self, f, t):
+ """Register a socket for polling.
+
+ @type f: C{int} or C{socket.socket}
+ @param f: the file descriptor, or the socket to get the file descriptor from
+ @type t: C{int}
+ @param t: the events to poll for
+
+ """
+
if type(f) != IntType:
f = f.fileno()
if (t & POLLIN):
@@ -31,12 +67,28 @@
remove(self.wlist, f)
def unregister(self, f):
+ """Unregister a socket from being polled.
+
+ @type f: C{int} or C{socket.socket}
+ @param f: the file descriptor, or the socket to get the file descriptor from
+
+ """
+
if type(f) != IntType:
f = f.fileno()
remove(self.rlist, f)
remove(self.wlist, f)
def poll(self, timeout = None):
+ """Poll the registered sockets for events.
+
+ @type timeout: unknown
+ @param timeout: unknown
+ @rtype: C{list} of (C{int}, C{int})
+ @return: the socket file descriptors and event types that have occurred on them
+
+ """
+
if self.rlist or self.wlist:
try:
r, w, e = select(self.rlist, self.wlist, [], timeout)
@@ -53,16 +105,39 @@
return result
def remove(list, item):
+ """Efficiently remove items from a sorted list.
+
+ If the item is not in the liost, does nothing.
+
+ @type list: C{list} of C{int}
+ @param list: the list
+ @type item: C{int}
+ @param item: the item to remove
+
+ """
+
i = bisect(list, item)
if i > 0 and list[i-1] == item:
del list[i-1]
def insert(list, item):
+ """Efficiently insert items into a sorted list.
+
+ If the item is already in the list, does nothing.
+
+ @type list: C{list} of C{int}
+ @param list: the list
+ @type item: C{int}
+ @param item: the item to insert
+
+ """
+
i = bisect(list, item)
if i == 0 or list[i-1] != item:
list.insert(i, item)
def test_remove():
+ """Test the remove function."""
x = [2, 4, 6]
remove(x, 2)
assert x == [4, 6]
@@ -89,6 +164,7 @@
assert x == []
def test_insert():
+ """Test the insert function."""
x = [2, 4]
insert(x, 1)
assert x == [1, 2, 4]
Modified: debtorrent/trunk/DebTorrent/subnetparse.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/subnetparse.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/subnetparse.py (original)
+++ debtorrent/trunk/DebTorrent/subnetparse.py Mon Jun 18 05:49:26 2007
@@ -1,8 +1,20 @@
# Written by John Hoffman
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Deal with all types of IP addresses and IP address subnets.
+
+ at type hexbinmap: C{dictionary}
+ at var hexbinmap: mapping from the hex characters to their binary string representation
+ at type chrbinmap: C{dictionary}
+ at var chrbinmap: mapping from the first 256 numbers to their binary string representation
+ at type ipv4addrmask: C{long}
+ at var ipv4addrmask: the address mask used to determine if the IP address
+ in C{long} format is v4 encapsulated in a v6 address
+
+"""
from bisect import bisect, insort
@@ -47,6 +59,15 @@
def to_bitfield_ipv4(ip):
+ """Convert an IPv4 address to it's binary string representation.
+
+ @type ip: C{string}
+ @param ip: the IPv4 address to convert
+ @rtype: C{string}
+ @return: the binary string of the IPv4 address
+
+ """
+
ip = ip.split('.')
if len(ip) != 4:
raise ValueError, "bad address"
@@ -56,6 +77,15 @@
return ''.join(b)
def to_bitfield_ipv6(ip):
+ """Convert an IPv6 address to it's binary string representation.
+
+ @type ip: C{string}
+ @param ip: the IPv6 address to convert
+ @rtype: C{string}
+ @return: the binary string of the IPv6 address
+
+ """
+
b = ''
doublecolon = False
@@ -95,7 +125,26 @@
ipv4addrmask = to_bitfield_ipv6('::ffff:0:0')[:96]
class IP_List:
+ """Stores multiple IP address subnets.
+
+ @type ipv4list: C{list} of C{string}
+ @ivar ipv4list: the binary strings representing the start of the IPv4
+ address that is in the subnet
+ @type ipv6list: C{list} of C{string}
+ @ivar ipv6list: the binary strings representing the start of the IPv6
+ address that is in the subnet
+
+ """
+
def __init__(self, entrylist=None):
+ """Initialize the instance.
+
+ @type entrylist: C{list} of (C{string}, C{int})
+ @param entrylist: the IP address and subnet length of the IP address
+ subnets to start with (optional, defaults to None)
+
+ """
+
self.ipv4list = []
self.ipv6list = []
if entrylist:
@@ -106,10 +155,27 @@
def __nonzero__(self):
+ """Check whether there are any IP address subnets stored.
+
+ @rtype: C{boolean}
+ @return: whether there are IP address subnets stored
+
+ """
+
return bool(self.ipv4list or self.ipv6list)
def _append(self, ip, depth = 256):
+ """Add a new IP address subnet to the end.
+
+ @type ip: C{string}
+ @param ip: an IP address in the subnet
+ @type depth: C{int}
+ @param depth: the length of the address subnet header
+ (optional, defaults to the entire IP address)
+
+ """
+
if ip.find(':') < 0: # IPv4
self.ipv4list.append(to_bitfield_ipv4(ip)[:depth])
else:
@@ -120,6 +186,16 @@
self.ipv6list.append(b[:depth])
def append(self, ip, depth = 256):
+ """Add a new IP address subnet in the correct order.
+
+ @type ip: C{string}
+ @param ip: an IP address in the subnet
+ @type depth: C{int}
+ @param depth: the length of the address subnet header
+ (optional, defaults to the entire IP address)
+
+ """
+
if ip.find(':') < 0: # IPv4
insort(self.ipv4list,to_bitfield_ipv4(ip)[:depth])
else:
@@ -131,6 +207,15 @@
def includes(self, ip):
+ """Determine whether the IP address is included in any of the subnets.
+
+ @type ip: C{string}
+ @param ip: the IP address to check
+ @rtype: C{boolean}
+ @return: whether the IP address is in one of the ranges
+
+ """
+
if not (self.ipv4list or self.ipv6list):
return False
if ip.find(':') < 0: # IPv4
@@ -152,6 +237,18 @@
def read_fieldlist(self, file): # reads a list from a file in the format 'ip/len <whatever>'
+ """Parse a file for lists of IP address subnets to add.
+
+ The file to parse must have lines in the format 'ip/len <whatever>'.
+ The 'whatever' will be ignored. If the subnet is only a single IP
+ address, omit the '/' and the 'len'. Empty lines will be ignored, as
+ will lines beginning with '#' which can be used for comments.
+
+ @type file: C{string}
+ @param file: the name of the file to parse
+
+ """
+
f = open(file, 'r')
while True:
line = f.readline()
@@ -185,6 +282,7 @@
def set_intranet_addresses(self):
+ """Add the (RFC 1918) private subnets and local subnets."""
self.append('127.0.0.1',8)
self.append('10.0.0.0',8)
self.append('172.16.0.0',12)
@@ -195,9 +293,19 @@
self.append('fec0::',16)
def set_ipv4_addresses(self):
+ """Add the IPv6 address subnet used to store IPv4 addresses."""
self.append('::ffff:0:0',96)
def ipv6_to_ipv4(ip):
+ """Convert an IPv6 address in the transition format to an IPv4 address.
+
+ @type ip: C{string}
+ @param ip: the IP address to convert
+ @rtype: C{string}
+ @return: the IPv4 address
+
+ """
+
ip = to_bitfield_ipv6(ip)
if not ip.startswith(ipv4addrmask):
raise ValueError, "not convertible to IPv4"
@@ -211,15 +319,41 @@
return x
def to_ipv4(ip):
+ """Determine whether the IP address is in the IPv4 format.
+
+ @type ip: C{string}
+ @param ip: the IP address to check
+ @rtype: C{boolean}
+ @return: whether the IP address is for IPv4
+
+ """
+
if is_ipv4(ip):
_valid_ipv4(ip)
return ip
return ipv6_to_ipv4(ip)
def is_ipv4(ip):
+ """Determine whether the IP address is in the IPv4 format.
+
+ @type ip: C{string}
+ @param ip: the IP address to check
+ @rtype: C{boolean}
+ @return: whether the IP address is for IPv4
+
+ """
+
return ip.find(':') < 0
def _valid_ipv4(ip):
+ """Determine whether the IP address is a valid IPv4 address.
+
+ @type ip: C{string}
+ @param ip: the IP address to check
+ @raise ValueError: if the address is not in the proper IPv4 format
+
+ """
+
ip = ip.split('.')
if len(ip) != 4:
raise ValueError
@@ -227,6 +361,15 @@
chr(int(i))
def is_valid_ip(ip):
+ """Determine whether the IP address is a valid IPv4 or IPv6 address.
+
+ @type ip: C{string}
+ @param ip: the IP address to check
+ @rtype: C{boolean}
+ @return: whether the IP address is valid
+
+ """
+
try:
if not ip:
return False
Modified: debtorrent/trunk/DebTorrent/torrentlistparse.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/torrentlistparse.py?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/torrentlistparse.py (original)
+++ debtorrent/trunk/DebTorrent/torrentlistparse.py Mon Jun 18 05:49:26 2007
@@ -1,8 +1,10 @@
# Written by John Hoffman
# Modified by Cameron Dale
# see LICENSE.txt for license information
+#
+# $Id$
-# $Id$
+"""Parse a file for a list of torrent hashes."""
from binascii import unhexlify
@@ -13,9 +15,20 @@
False = 0
-# parses a list of torrent hashes, in the format of one hash per line in hex format
+def parsetorrentlist(filename, parsed):
+ """Parse a file for a list of torrent hashes.
+
+ The format is one hash per line in hex format (40 characters).
-def parsetorrentlist(filename, parsed):
+ @type filename: C{string}
+ @param filename: the file to parse
+ @type parsed: C{dictionary}
+ @param parsed: the cache of all the torrents ever found
+ @rtype: (C{dictionary}, C{dictionary}, C{dictionary})
+ @return: the cache of all the torrents found, the new torrents that were
+ found, the torrents that are now missing
+
+ """
new_parsed = {}
added = {}
removed = parsed
Modified: debtorrent/trunk/TODO
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/TODO?rev=115&op=diff
==============================================================================
--- debtorrent/trunk/TODO (original)
+++ debtorrent/trunk/TODO Mon Jun 18 05:49:26 2007
@@ -74,19 +74,15 @@
later versions, and these statistics may not be needed.
-Proposal for modifications to apt-ftparchive
+Switch to using the poll() system call instead of the select module
-Modifications need to be made to the debian archive management program
-apt-ftparchive to support some of the upcoming features of debtorrent. Primarily,
-the large files in a Packages file need to be broken up into sub-pieces, and
-SHA1 hashes of the sub-pieces created. These would need to be cached, so as to
-minimize the computation needed to generate a Packages file. Then, the tracker
-address needs to be added to Release files, along with any other needed metainfo.
-Finally, large Packages files need to be broken down into smaller sub-pieces, and
-SHA1 sums generated for the sub-pieces generated. These could be stored in the
-Release file, or in separate files (Packages.pieces). For each of these, some
-numbers are needed on the amount of data needed to be added to the files, and
-how much computation will be required to generate it.
+The poll() system call, supported on most Unix systems, provides better
+scalability for network servers that service many, many clients at the same
+time. poll() scales better because the system call only requires listing the
+file descriptors of interest, while select() builds a bitmap, turns on bits
+for the fds of interest, and then afterward the whole bitmap has to be
+linearly scanned again. select() is O(highest file descriptor), while poll()
+is O(number of file descriptors).
Consider Sources
More information about the Debtorrent-commits
mailing list