[Debtorrent-commits] r79 - in /debtorrent/branches/http-listen/DebTorrent: BT1/track.py zurllib.py

camrdale-guest at users.alioth.debian.org camrdale-guest at users.alioth.debian.org
Sat Jun 2 05:01:04 UTC 2007


Author: camrdale-guest
Date: Sat Jun  2 05:01:03 2007
New Revision: 79

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

Modified:
    debtorrent/branches/http-listen/DebTorrent/BT1/track.py
    debtorrent/branches/http-listen/DebTorrent/zurllib.py

Modified: debtorrent/branches/http-listen/DebTorrent/BT1/track.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/BT1/track.py?rev=79&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/BT1/track.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/BT1/track.py Sat Jun  2 05:01:03 2007
@@ -1,8 +1,22 @@
 # Written by Bram Cohen
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
-
+#
 # $Id$
+
+"""Tools to track a download swarm.
+
+ at type defaults: C{list} of (C[string}, C{unknown}, C{string})
+ at var defaults: the parameter names, default values, and descriptions
+ at type alas: C{string}
+ at var alas: the message to send when the data is not found
+ at type local_IPs: L{DebTorrent.subnetparse.IP_List}
+ at var local_IPs: the list of IP subnets that are considered local
+ at type http_via_filter: C{Regular Expression}
+ at var http_via_filter: the regular expression object to search 'via' 
+    header information for the NAT IP address
+
+"""
 
 from DebTorrent.parseargs import parseargs, formatDefinitions
 from DebTorrent.RawServer import RawServer, autodetect_ipv6, autodetect_socket_style
@@ -100,6 +114,14 @@
   ]
 
 def statefiletemplate(x):
+    """Check the saved state file for corruption.
+    
+    @type x: C{dictionary}
+    @param x: the dictionary of information retrieved from the state file
+    @raise ValueError: if the state file info is corrupt
+    
+    """
+    
     if type(x) != DictType:
         raise ValueError
     for cname, cinfo in x.items():
@@ -159,6 +181,16 @@
 
 
 def isotime(secs = None):
+    """Create an ISO formatted string of the time.
+    
+    @type secs: C{float}
+    @param secs: number of seconds since the epoch 
+        (optional, default is to use the current time)
+    @rtype: C{string}
+    @return: the ISO formatted string representation of the time
+    
+    """
+    
     if secs == None:
         secs = time()
     return strftime('%Y-%m-%d %H:%M UTC', gmtime(secs))
@@ -166,6 +198,15 @@
 http_via_filter = re.compile(' for ([0-9.]+)\Z')
 
 def _get_forwarded_ip(headers):
+    """Extract the unNATed IP address from the headers.
+    
+    @type headers: C{dictionary}
+    @param headers: the headers received from the client
+    @rtype: C{string}
+    @return: the extracted IP address
+    
+    """
+    
     header = headers.get('x-forwarded-for')
     if header:
         try:
@@ -192,12 +233,33 @@
     return header
 
 def get_forwarded_ip(headers):
+    """Extract the unNATed IP address from the headers.
+    
+    @type headers: C{dictionary}
+    @param headers: the headers received from the client
+    @rtype: C{string}
+    @return: the extracted IP address (or None if one could not be extracted)
+    
+    """
+    
     x = _get_forwarded_ip(headers)
     if not is_valid_ip(x) or local_IPs.includes(x):
         return None
     return x
 
 def compact_peer_info(ip, port):
+    """Create a compact representation of peer contact info.
+    
+    @type ip: C{string}
+    @param ip: the IP address of the peer
+    @type port: C{int}
+    @param port: the port number to contact the peer on
+    @rtype: C{string}
+    @return: the compact representation (or the empty string if there is no
+        compact representation)
+    
+    """
+    
     try:
         s = ( ''.join([chr(int(i)) for i in ip.split('.')])
               + chr((port & 0xFF00) >> 8) + chr(port & 0xFF) )
@@ -208,7 +270,122 @@
     return s
 
 class Tracker:
+    """Track a download swarm.
+    
+    @type config: C{dictionary}
+    @ivar config: the configuration parameters
+    @type response_size: unknown
+    @ivar response_size: unknown
+    @type dfile: C{string}
+    @ivar dfile: the state file to use when saving the current state
+    @type natcheck: C{int}
+    @ivar natcheck: how many times to check if a downloader is behind a NAT
+    @type parse_dir_interval: C{int}
+    @ivar parse_dir_interval: seconds between reloading of the allowed 
+        directory or file, and the lists of allowed and banned IPs
+    @type favicon: C{string}
+    @ivar favicon: file containing x-icon data
+    @type rawserver: L{DebTorrent.RawServer}
+    @ivar rawserver: the server to use for scheduling
+    @type cached: unknown
+    @ivar cached: unknown
+    @type cached_t: unknown
+    @ivar cached_t: unknown
+    @type times: unknown
+    @ivar times: unknown
+    @type state: C{dictionary}
+    @ivar state: the current state information for the tracking
+    @type seedcount: unknown
+    @ivar seedcount: unknown
+    @type allowed_IPs: unknown
+    @ivar allowed_IPs: unknown
+    @type banned_IPs: unknown
+    @ivar banned_IPs: unknown
+    @type allowed_ip_mtime: unknown
+    @ivar allowed_ip_mtime: unknown
+    @type banned_ip_mtime: unknown
+    @ivar banned_ip_mtime: unknown
+    @type only_local_override_ip: C{boolean}
+    @ivar only_local_override_ip: whether to ignore the "ip" parameter from 
+        machines which aren't on local network IPs
+    @type downloads: unknown
+    @ivar downloads: unknown
+    @type completed: unknown
+    @ivar completed: unknown
+    @type becache: C{dictionary}
+    @ivar becache: keys are the infohashes, values are the sets of peers.
+        peer set format::
+            [[l0, s0], [l1, s1], ...]
+                l,s   = lists of leechers and seeders
+                l0,s0 = compact representation, don't require crypto
+                l1,s1 = compact representation, support crypto
+                l2,s2 = [compact representation, crypto_flag], for all peers
+            additionally, if --compact_reqd = 0:
+                l3,s3 = [ip,port,peerid] for all peers
+                l4,l4 = [ip,port] for all peers
+    @type cache_default: unknown
+    @ivar cache_default: unknown
+    @type trackerid: unknown
+    @ivar trackerid: unknown
+    @type reannounce_interval: C{int}
+    @ivar reannounce_interval: seconds downloaders should wait between reannouncements
+    @type save_dfile_interval: C{int}
+    @ivar save_dfile_interval: seconds between saving the state file
+    @type show_names: C{boolean}
+    @ivar show_names: whether to display names from allowed dir
+    @type prevtime: unknown
+    @ivar prevtime: unknown
+    @type timeout_downloaders_interval: C{int}
+    @ivar timeout_downloaders_interval: seconds between expiring downloaders
+    @type logfile: unknown
+    @ivar logfile: unknown
+    @type log: unknown
+    @ivar log: unknown
+    @type allow_get: unknown
+    @ivar allow_get: unknown
+    @type t2tlist: L{T2T.T2TList}
+    @ivar t2tlist: unknown
+    @type allowed: unknown
+    @ivar allowed: unknown
+    @type allowed_list_mtime: unknown
+    @ivar allowed_list_mtime: unknown
+    @type allowed_dir_files: unknown
+    @ivar allowed_dir_files: unknown
+    @type allowed_dir_blocked: unknown
+    @ivar allowed_dir_blocked: unknown
+    @type uq_broken: unknown
+    @ivar uq_broken: unknown
+    @type keep_dead: unknown
+    @ivar keep_dead: unknown
+    @type Filter: unknown
+    @ivar Filter: unknown
+    @type is_aggregator: unknown
+    @ivar is_aggregator: unknown
+    @type aggregator_key: unknown
+    @ivar aggregator_key: unknown
+    @type aggregate_forward: unknown
+    @ivar aggregate_forward: unknown
+    @type aggregate_password: unknown
+    @ivar aggregate_password: unknown
+    @type dedicated_seed_id: unknown
+    @ivar dedicated_seed_id: unknown
+    @type is_seeded: unknown
+    @ivar is_seeded: unknown
+    @type cachetime: unknown
+    @ivar cachetime: unknown
+    
+    """
+    
     def __init__(self, config, rawserver):
+        """Initialize the instance.
+        
+        @type config: C{dictionary}
+        @param config: the configuration parameters
+        @type rawserver: L{DebTorrent.RawServer}
+        @param rawserver: the server to use for scheduling
+        
+        """
+        
         self.config = config
         self.response_size = config['response_size']
         self.dfile = config['dfile']
@@ -261,14 +438,7 @@
         self.completed = self.state.setdefault('completed', {})
 
         self.becache = {}
-        ''' format: infohash: [[l0, s0], [l1, s1], ...]
-                l0,s0 = compact, not requirecrypto=1
-                l1,s1 = compact, only supportcrypto=1
-                l2,s2 = [compact, crypto_flag], all peers
-            if --compact_reqd 0:
-                l3,s3 = [ip,port,id]
-                l4,l4 = [ip,port] nopeerid
-        '''
+
         if config['compact_reqd']:
             x = 3
         else:
@@ -320,6 +490,19 @@
 
         if config['hupmonitor']:
             def huphandler(signum, frame, self = self):
+                """Function to handle SIGHUP signals.
+                
+                Reopens the log file when a SIGHUP is received.
+                
+                @type signum: unknown
+                @param signum: ignored
+                @type frame: unknown
+                @param frame: ignored
+                @type self: L{Track}
+                @param self: the Track instance to reopen the log of
+                
+                """
+                
                 try:
                     self.log.close ()
                     self.log = open(self.logfile,'a')
@@ -397,10 +580,18 @@
         self.cachetimeupdate()
 
     def cachetimeupdate(self):
+        """Update the L{cachetime} every second."""
         self.cachetime += 1     # raw clock, but more efficient for cache
         self.rawserver.add_task(self.cachetimeupdate,1)
 
     def aggregate_senddata(self, query):
+        """Fork sending data to a tracker aggregator.
+        
+        @type query: C{string}
+        @param query: the query to send
+        
+        """
+        
         url = self.aggregate_forward+'?'+query
         if self.aggregate_password is not None:
             url += '&password='+self.aggregate_password
@@ -408,8 +599,17 @@
         rq.setDaemon(False)
         rq.start()
 
-    def _aggregate_senddata(self, url):     # just send, don't attempt to error check,
-        try:                                # discard any returned data
+    def _aggregate_senddata(self, url):
+        """Send a URL request to a tracker data aggregator.
+                     
+        Just send, don't attempt to error check, discard any returned data.
+        
+        @type url: C{string}
+        @param url: the URL to request
+
+        """
+        
+        try:
             h = urlopen(url)
             h.read()
             h.close()
@@ -418,6 +618,16 @@
 
 
     def get_infopage(self):
+        """Format the info page to display for normal browsers.
+        
+        Formats the currently tracked torrents into a table in human-readable
+        format to display in a browser window.
+        
+        @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+        @return: the HTTP status code, status message, headers, and message body
+        
+        """
+        
         try:
             if not self.config['show_infopage']:
                 return (404, 'Not Found', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
@@ -510,6 +720,18 @@
 
 
     def scrapedata(self, hash, return_name = True):
+        """Retrieve the scrape data for a single torrent.
+        
+        @type hash: C{string}
+        @param hash: the infohash of the torrent to get scrape data for
+        @type return_name: C{boolean}
+        @param return_name: whether to return the name of the torrent
+            (optional, defaults to True)
+        @rtype: C{dictionary}
+        @return: the scrape data for the torrent
+        
+        """
+        
         l = self.downloads[hash]
         n = self.completed.get(hash, 0)
         c = self.seedcount[hash]
@@ -520,6 +742,16 @@
         return (f)
 
     def get_scrape(self, paramslist):
+        """Get the scrape data for all the active torrents.
+        
+        @type paramslist: C{dictionary}
+        @param paramslist: the query parameters from the GET request
+        @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+        @return: the HTTP status code, status message, headers, and bencoded 
+            message body
+       
+        """
+        
         fs = {}
         if paramslist.has_key('info_hash'):
             if self.config['scrape_allowed'] not in ['specific', 'full']:
@@ -549,19 +781,41 @@
 
 
     def get_file(self, hash):
-         if not self.allow_get:
-             return (400, 'Not Authorized', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'},
-                 'get function is not available with this tracker.')
-         if not self.allowed.has_key(hash):
-             return (404, 'Not Found', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
-         fname = self.allowed[hash]['file']
-         fpath = self.allowed[hash]['path']
-         return (200, 'OK', {'Content-Type': 'application/x-debtorrent',
-             'Content-Disposition': 'attachment; filename=' + fname},
-             open(fpath, 'rb').read())
+        """Get the metainfo file for a torrent.
+        
+        @type hash: C{string}
+        @param hash: the infohash of the torrent to get the metainfo of
+        @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+        @return: the HTTP status code, status message, headers, and bencoded 
+            metainfo file
+        
+        """
+        
+        if not self.allow_get:
+            return (400, 'Not Authorized', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'},
+                'get function is not available with this tracker.')
+        if not self.allowed.has_key(hash):
+            return (404, 'Not Found', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
+        fname = self.allowed[hash]['file']
+        fpath = self.allowed[hash]['path']
+        return (200, 'OK', {'Content-Type': 'application/x-debtorrent',
+            'Content-Disposition': 'attachment; filename=' + fname},
+            open(fpath, 'rb').read())
 
 
     def check_allowed(self, infohash, paramslist):
+        """Determine whether the tracker is tracking this torrent.
+        
+        @type infohash: C{string}
+        @param infohash: the infohash of the torrent to check
+        @type paramslist: C{dictionary}
+        @param paramslist: the query parameters from the GET request
+        @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+        @return: the HTTP status code, status message, headers, and bencoded 
+            message body if the request is not allowed, or None if it is
+
+        """
+        
         if ( self.aggregator_key is not None
                 and not ( paramslist.has_key('password')
                         and paramslist['password'][0] == self.aggregator_key ) ):
@@ -595,12 +849,42 @@
 
 
     def add_data(self, infohash, event, ip, paramslist):
+        """Add the received data from the peer to the cache.
+        
+        @type infohash: C{string}
+        @param infohash: the infohash of the torrent to add to
+        @type event: C{string}
+        @param event: the type of event being reported by the peer
+        @type ip: C{string}
+        @param ip: the IP address of the peer
+        @type paramslist: C{dictionary}
+        @param paramslist: the query parameters from the GET request
+        @rtype: C{int}
+        @return: the number of peers to return in a peer list
+        @raise ValueError: if the request from the peer is corrupt
+        
+        """
+        
         peers = self.downloads.setdefault(infohash, {})
         ts = self.times.setdefault(infohash, {})
         self.completed.setdefault(infohash, 0)
         self.seedcount.setdefault(infohash, 0)
 
         def params(key, default = None, l = paramslist):
+            """Get the user parameter, or the default.
+            
+            @type key: C{string}
+            @param key: the parameter to get
+            @type default: C{string}
+            @param default: the default value to use if no parameter is set
+                (optional, defaults to None)
+            @type l: C{dictionary}
+            @param l: the user parameters (optional, defaults to L{paramslist})
+            @rtype: C{string}
+            @return: the parameter's value
+            
+            """
+            
             if l.has_key(key):
                 return l[key][0]
             return default
@@ -740,6 +1024,28 @@
 
     def peerlist(self, infohash, stopped, tracker, is_seed,
                  return_type, rsize, supportcrypto):
+        """Create a list of peers to return to the client.
+        
+        @type infohash: C{string}
+        @param infohash: the infohash of the torrent to get peers from
+        @type stopped: C{boolean}
+        @param stopped: whether the peer has stopped
+        @type tracker: C{boolean}
+        @param tracker: whether the peer is a tracker
+        @type is_seed: C{boolean}
+        @param is_seed: whether the peer is currently seeding
+        @type return_type: C{int}
+        @param return_type: the format of the list to return (compact, ...)
+        @type rsize: C{int}
+        @param rsize: the number of peers to return
+        @type supportcrypto: C{boolean}
+        @param supportcrypto: whether the peer supports encrypted communication
+            (not used)
+        @rtype: C{dictionary}
+        @return: the info to return to the client
+        
+        """
+        
         data = {}    # return data
         seeds = self.seedcount[infohash]
         data['complete'] = seeds
@@ -840,6 +1146,23 @@
 
 
     def get(self, connection, path, headers):
+        """Respond to a GET request to the tracker.
+        
+        Process a GET request from a peer/tracker/browser. Process the request,
+        calling the helper functions above if needed. Return the response to
+        be returned to the requester.
+        
+        @type connection: unknown
+        @param connection: the conection the request came in on
+        @type path: C{string}
+        @param path: the URL being requested
+        @type headers: C{dictionary}
+        @param headers: the headers from the request
+        @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+        @return: the HTTP status code, status message, headers, and message body
+        
+        """
+        
         real_ip = connection.get_ip()
         ip = real_ip
         if is_ipv4(ip):
@@ -868,6 +1191,20 @@
 
         paramslist = {}
         def params(key, default = None, l = paramslist):
+            """Get the user parameter, or the default.
+            
+            @type key: C{string}
+            @param key: the parameter to get
+            @type default: C{string}
+            @param default: the default value to use if no parameter is set
+                (optional, defaults to None)
+            @type l: C{dictionary}
+            @param l: the user parameters (optional, defaults to L{paramslist})
+            @rtype: C{string}
+            @return: the parameter's value
+            
+            """
+            
             if l.has_key(key):
                 return l[key][0]
             return default
@@ -962,6 +1299,21 @@
 
 
     def natcheckOK(self, infohash, peerid, ip, port, peer):
+        """Add the unNATed peer to the cache.
+        
+        @type infohash: C{string}
+        @param infohash: the infohash of the torrent the peer is in
+        @type peerid: C{string}
+        @param peerid: the peer ID of the peer
+        @type ip: C{string}
+        @param ip: the IP address of the peer
+        @type port: C{int}
+        @param port: the port to contact the peer on
+        @type peer: C{dictionary}
+        @param peer: various information about the peer
+        
+        """
+        
         seed = not peer['left']
         bc = self.becache.setdefault(infohash,self.cache_default)
         cp = compact_peer_info(ip, port)
@@ -978,12 +1330,40 @@
 
 
     def natchecklog(self, peerid, ip, port, result):
+        """Log the results of any NAT checks performed.
+        
+        @type peerid: C{string}
+        @param peerid: the peer ID of the peer
+        @type ip: C{string}
+        @param ip: the IP address of the peer
+        @type port: C{int}
+        @param port: the port to contact the peer on
+        @type result: C{int}
+        @param result: the HTTP status code result of the NAT check
+        
+        """
+        
         year, month, day, hour, minute, second, a, b, c = localtime(time())
         print '%s - %s [%02d/%3s/%04d:%02d:%02d:%02d] "!natcheck-%s:%i" %i 0 - -' % (
             ip, quote(peerid), day, months[month], year, hour, minute, second,
             ip, port, result)
 
     def connectback_result(self, result, downloadid, peerid, ip, port):
+        """Process a NAT check attempt and result.
+        
+        @type result: C{boolean}
+        @param result: whether the NAT check was successful
+        @type downloadid: C{string}
+        @param downloadid: the infohash of the torrent the peer is in
+        @type peerid: C{string}
+        @param peerid: the peer ID of the peer
+        @type ip: C{string}
+        @param ip: the IP address of the peer
+        @type port: C{int}
+        @param port: the port to contact the peer on
+        
+        """
+        
         record = self.downloads.get(downloadid,{}).get(peerid)
         if ( record is None 
                  or (record['ip'] != ip and record.get('given ip') != ip)
@@ -1009,6 +1389,7 @@
 
 
     def remove_from_state(self, *l):
+        """Remove all the input parameter names from the current state."""
         for s in l:
             try:
                 del self.state[s]
@@ -1016,6 +1397,7 @@
                 pass
 
     def save_state(self):
+        """Save the state file to disk."""
         self.rawserver.add_task(self.save_state, self.save_dfile_interval)
         h = open(self.dfile, 'wb')
         h.write(bencode(self.state))
@@ -1023,6 +1405,7 @@
 
 
     def parse_allowed(self):
+        """Periodically parse the directory and list for allowed torrents."""
         self.rawserver.add_task(self.parse_allowed, self.parse_dir_interval)
 
         if self.config['allowed_dir']:
@@ -1057,6 +1440,7 @@
 
 
     def read_ip_lists(self):
+        """Periodically parse the allowed and banned IPs lists."""
         self.rawserver.add_task(self.read_ip_lists,self.parse_dir_interval)
             
         f = self.config['allowed_ips']
@@ -1079,6 +1463,15 @@
                 
 
     def delete_peer(self, infohash, peerid):
+        """Delete all cached data for the peer.
+        
+        @type infohash: C{string}
+        @param infohash: the infohash of the torrent to delete the peer from
+        @type peerid: C{string}
+        @param peerid: the peer ID of the peer to delete
+        
+        """
+        
         dls = self.downloads[infohash]
         peer = dls[peerid]
         if not peer['left']:
@@ -1093,6 +1486,7 @@
         del dls[peerid]
 
     def expire_downloaders(self):
+        """Periodically remove all old downloaders from the cached data."""
         for x in self.times.keys():
             for myid, t in self.times[x].items():
                 if t < self.prevtime:
@@ -1109,6 +1503,13 @@
 
 
 def track(args):
+    """Start the server and tracker.
+    
+    @type args: C{list}
+    @param args: the command line arguments to the tracker
+    
+    """
+    
     if len(args) == 0:
         print formatDefinitions(defaults, 80)
         return
@@ -1128,6 +1529,15 @@
     print '# Shutting down: ' + isotime()
 
 def size_format(s):
+    """Format a byte size for reading by the user.
+    
+    @type s: C{long}
+    @param s: the number of bytes
+    @rtype: C{string}
+    @return: the formatted size with appropriate units
+    
+    """
+    
     if (s < 1024):
         r = str(s) + 'B'
     elif (s < 1048576):

Modified: debtorrent/branches/http-listen/DebTorrent/zurllib.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/zurllib.py?rev=79&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/zurllib.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/zurllib.py Sat Jun  2 05:01:03 2007
@@ -1,8 +1,17 @@
 # Written by John Hoffman
 # Modified by Cameron Dale
 # see LICENSE.txt for license information
+#
+# $Id$
 
-# $Id$
+"""A high-level fetcher for WWW data, similar to the urllib module.
+
+ at type VERSION: C{string}
+ at var VERSION: the User-Agent header to send on all connections
+ at type MAX_REDIRECTS: C{int}
+ at var MAX_REDIRECTS: the maximum number of redirects to follow
+
+"""
 
 from httplib import HTTPConnection, HTTPSConnection, HTTPException
 from urlparse import urlparse
@@ -17,16 +26,22 @@
 MAX_REDIRECTS = 10
 
 
-class btHTTPcon(HTTPConnection): # attempt to add automatic connection timeout
+class btHTTPcon(HTTPConnection):
+    """Attempt to add automatic connection timeout to HTTPConnection."""
+    
     def connect(self):
+        """Redefine the connect to include a socket timeout."""
         HTTPConnection.connect(self)
         try:
             self.sock.settimeout(30)
         except:
             pass
 
-class btHTTPScon(HTTPSConnection): # attempt to add automatic connection timeout
+class btHTTPScon(HTTPSConnection):
+    """Attempt to add automatic connection timeout to HTTPSConnection."""
+
     def connect(self):
+        """Redefine the connect to include a socket timeout."""
         HTTPSConnection.connect(self)
         try:
             self.sock.settimeout(30)
@@ -34,12 +49,41 @@
             pass 
 
 class urlopen:
+    """Opens a URL for reading.
+    
+    @type tries: C{int}
+    @ivar tries: the number of attempts to open it so far
+    @type error_return: C{dictionary}
+    @ivar error_return: the bencoded returned data if an error occurred
+    @type connection: L{btHTTPcon} or L{btHTTPScon}
+    @ivar connection: the connection to the server
+    @type response: C{httplib.HTTPResponse}
+    @ivar response: the response from the server
+    
+    """
+    
     def __init__(self, url):
+        """Initialize the instance and call the open method.
+        
+        @type url: C{string}
+        @param url: the URL to open
+        
+        """
+        
         self.tries = 0
         self._open(url.strip())
         self.error_return = None
 
     def _open(self, url):
+        """Open a connection and request the URL, saving the response.
+        
+        @type url: C{string}
+        @param url: the URL to open
+        @raise IOError: if there was a problem with the URL, or if the 
+            server returned an error
+        
+        """
+        
         self.tries += 1
         if self.tries > MAX_REDIRECTS:
             raise IOError, ('http error', 500,
@@ -84,11 +128,24 @@
             raise IOError, ('http error', status, self.response.reason)
 
     def read(self):
+        """Read the response data from the previous request.
+        
+        @rtype: C{string}
+        @return: the response, or the error if an error occurred
+        
+        """
         if self.error_return:
             return self.error_return
         return self._read()
 
     def _read(self):
+        """Read the response data and maybe decompress it.
+        
+        @rtype: C{string}
+        @return: the processed response data
+        
+        """
+        
         data = self.response.read()
         if self.response.getheader('Content-Encoding','').find('gzip') >= 0:
             try:
@@ -100,4 +157,5 @@
         return data
 
     def close(self):
+        """Closes the connection to the server."""
         self.connection.close()




More information about the Debtorrent-commits mailing list