Author: aum
Date: 2006-05-14 05:23:40 +0000 (Sun, 14 May 2006)
New Revision: 8697

Modified:
   trunk/apps/pyFreenet/code.leo
   trunk/apps/pyFreenet/fcp.py
   
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
   
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
   trunk/apps/pyFreenet/html/private/SocketServer.BaseServer-class.html
   trunk/apps/pyFreenet/html/private/SocketServer.TCPServer-class.html
   trunk/apps/pyFreenet/html/private/SocketServer.ThreadingMixIn-class.html
   trunk/apps/pyFreenet/html/private/exceptions.Exception-class.html
   trunk/apps/pyFreenet/html/private/fcp-module.html
   trunk/apps/pyFreenet/html/private/fcp.ConnectionRefused-class.html
   trunk/apps/pyFreenet/html/private/fcp.FCPException-class.html
   trunk/apps/pyFreenet/html/private/fcp.FCPGetFailed-class.html
   trunk/apps/pyFreenet/html/private/fcp.FCPNodeConnection-class.html
   trunk/apps/pyFreenet/html/private/fcp.FCPProtocolError-class.html
   trunk/apps/pyFreenet/html/private/fcp.FCPPutFailed-class.html
   trunk/apps/pyFreenet/html/private/fcp.JobTicket-class.html
   trunk/apps/pyFreenet/html/private/fcpxmlrpc-module.html
   trunk/apps/pyFreenet/html/private/fcpxmlrpc.FCPXMLRPCServer-class.html
   
trunk/apps/pyFreenet/html/private/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
   trunk/apps/pyFreenet/html/private/help.html
   trunk/apps/pyFreenet/html/private/indices.html
   trunk/apps/pyFreenet/html/private/sitemgr-module.html
   trunk/apps/pyFreenet/html/private/sitemgr.SiteMgr-class.html
   trunk/apps/pyFreenet/html/private/trees.html
   
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
   
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
   trunk/apps/pyFreenet/html/public/SocketServer.TCPServer-class.html
   trunk/apps/pyFreenet/html/public/SocketServer.ThreadingMixIn-class.html
   trunk/apps/pyFreenet/html/public/exceptions.Exception-class.html
   trunk/apps/pyFreenet/html/public/fcp-module.html
   trunk/apps/pyFreenet/html/public/fcp.ConnectionRefused-class.html
   trunk/apps/pyFreenet/html/public/fcp.FCPException-class.html
   trunk/apps/pyFreenet/html/public/fcp.FCPGetFailed-class.html
   trunk/apps/pyFreenet/html/public/fcp.FCPNodeConnection-class.html
   trunk/apps/pyFreenet/html/public/fcp.FCPProtocolError-class.html
   trunk/apps/pyFreenet/html/public/fcp.FCPPutFailed-class.html
   trunk/apps/pyFreenet/html/public/fcp.JobTicket-class.html
   trunk/apps/pyFreenet/html/public/fcpxmlrpc-module.html
   trunk/apps/pyFreenet/html/public/fcpxmlrpc.FCPXMLRPCServer-class.html
   
trunk/apps/pyFreenet/html/public/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
   trunk/apps/pyFreenet/html/public/help.html
   trunk/apps/pyFreenet/html/public/indices.html
   trunk/apps/pyFreenet/html/public/sitemgr-module.html
   trunk/apps/pyFreenet/html/public/sitemgr.SiteMgr-class.html
   trunk/apps/pyFreenet/html/public/trees.html
   trunk/apps/pyFreenet/sitemgr.py
   trunk/apps/pyFreenet/updatesites.py
Log:
Added support for persistent requests:
 - get, put and putdir methods accept 'persistence' keyword arg
  - on connect, the persistence messages sent by the mode for prior
     persistent jobs are captured into self.persistentJobs
Improved control over logging in sitemgr.py and updatesites.py


Modified: trunk/apps/pyFreenet/code.leo
===================================================================
--- trunk/apps/pyFreenet/code.leo       2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/code.leo       2006-05-14 05:23:40 UTC (rev 8697)
@@ -1,36 +1,37 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <leo_file>
 <leo_header file_format="2" tnodes="0" max_tnode_index="6" clone_windows="0"/>
-<globals body_outline_ratio="0.313893653516">
-       <global_window_position top="87" left="116" height="649" width="1141"/>
+<globals body_outline_ratio="0.260297984224">
+       <global_window_position top="33" left="118" height="649" width="1141"/>
        <global_log_window_position top="0" left="0" height="0" width="0"/>
 </globals>
 <preferences/>
 <find_panel_settings/>
 <vnodes>
 <v t="aum.20060506215300" a="E"><vh>PyFCP</vh></v>
-<v t="aum.20060513180215" a="E"><vh>Release files</vh>
+<v t="aum.20060513180215"><vh>Release files</vh>
 <v t="aum.20060513180215.1" tnodeList="aum.20060513180215.1"><vh>@nosent 
README</vh></v>
 <v t="aum.20060513180716" tnodeList="aum.20060513180716"><vh>@nosent 
INSTALL</vh></v>
 <v t="aum.20060513180932" tnodeList="aum.20060513180932"><vh>@nosent 
AUTHORS</vh></v>
 <v t="aum.20060513181137" tnodeList="aum.20060513181137"><vh>@nosent 
COPYING</vh></v>
 <v t="aum.20060513181205" tnodeList="aum.20060513181205"><vh>@nosent 
BUGS</vh></v>
 <v t="aum.20060513181313" tnodeList="aum.20060513181313"><vh>@nosent 
CHANGELOG</vh></v>
-<v t="aum.20060513182312" a="V" tnodeList="aum.20060513182312"><vh>@nosent 
release.py</vh></v>
+<v t="aum.20060513182312" tnodeList="aum.20060513182312"><vh>@nosent 
release.py</vh></v>
 </v>
 <v t="aum.20060513073239" a="E"><vh>Main library module</vh>
-<v t="aum.20060506215707" 
tnodeList="aum.20060506215707,aum.20060506215707.1,aum.20060506220237,aum.20060506215707.2,aum.20060506215707.3,aum.20060506220237.1,aum.20060506220237.2,aum.20060506224238,aum.20060506231352.1,aum.20060506231352,aum.20060507003931,aum.20060511001853,aum.20060511205201,aum.20060506232639,aum.20060506232639.1,aum.20060511222538,aum.20060512101715,aum.20060511205201.1,aum.20060511205201.2,aum.20060506223545,aum.20060506224238.1,aum.20060506231352.2,aum.20060506220856,aum.20060506222005,aum.20060507124316,aum.20060511103841,aum.20060511103841.1,aum.20060511103952,aum.20060511103952.1,aum.20060512181209,aum.20060512102840,aum.20060509184020.1,aum.20060509184020.2,aum.20060509224119,aum.20060509224221"><vh>@nosent
 fcp.py</vh>
+<v t="aum.20060506215707" a="E" 
tnodeList="aum.20060506215707,aum.20060506215707.1,aum.20060506220237,aum.20060506215707.2,aum.20060506215707.3,aum.20060506220237.1,aum.20060506220237.2,aum.20060506224238,aum.20060506231352.1,aum.20060506231352,aum.20060507003931,aum.20060511001853,aum.20060514124642,aum.20060511205201,aum.20060506232639,aum.20060506232639.1,aum.20060511222538,aum.20060512101715,aum.20060511205201.1,aum.20060511205201.2,aum.20060506223545,aum.20060506224238.1,aum.20060506231352.2,aum.20060506220856,aum.20060506222005,aum.20060507124316,aum.20060511103841,aum.20060511103841.1,aum.20060511103952,aum.20060511103952.1,aum.20060514134235,aum.20060512181209,aum.20060514162944,aum.20060514124934,aum.20060512102840,aum.20060514164052,aum.20060509184020.1,aum.20060509184020.2,aum.20060509224119,aum.20060509224221"><vh>@nosent
 fcp.py</vh>
 <v t="aum.20060506215707.1"><vh>imports</vh></v>
 <v t="aum.20060506220237"><vh>exceptions</vh></v>
 <v t="aum.20060506215707.2"><vh>globals</vh></v>
 <v t="aum.20060506215707.3" a="E"><vh>class FCPNodeConnection</vh>
-<v t="aum.20060506220237.1"><vh>__init__</vh></v>
+<v t="aum.20060506220237.1" a="V"><vh>__init__</vh></v>
 <v t="aum.20060506220237.2"><vh>__del__</vh></v>
 <v t="aum.20060506224238" a="E"><vh>High Level Methods</vh>
 <v t="aum.20060506231352.1"><vh>genkey</vh></v>
 <v t="aum.20060506231352"><vh>get</vh></v>
 <v t="aum.20060507003931"><vh>put</vh></v>
 <v t="aum.20060511001853"><vh>putdir</vh></v>
+<v t="aum.20060514124642"><vh>refreshPersistentRequests</vh></v>
 <v t="aum.20060511205201"><vh>shutdown</vh></v>
 </v>
 <v t="aum.20060506232639" a="E"><vh>Manager Thread</vh>
@@ -52,8 +53,12 @@
 <v t="aum.20060511103841.1"><vh>__init__</vh></v>
 <v t="aum.20060511103952"><vh>isDone</vh></v>
 <v t="aum.20060511103952.1"><vh>wait</vh></v>
+<v t="aum.20060514134235"><vh>getResult</vh></v>
 <v t="aum.20060512181209"><vh>callback</vh></v>
+<v t="aum.20060514162944"><vh>cancel</vh></v>
+<v t="aum.20060514124934"><vh>_appendMsg</vh></v>
 <v t="aum.20060512102840"><vh>_putResult</vh></v>
+<v t="aum.20060514164052"><vh>__repr__</vh></v>
 </v>
 <v t="aum.20060509184020.1" a="E"><vh>util funcs</vh>
 <v t="aum.20060509184020.2"><vh>toBool</vh></v>
@@ -84,7 +89,7 @@
 </v>
 </v>
 <v t="aum.20060513073239.2" a="E"><vh>Freesite management</vh>
-<v t="aum.20060511101147" 
tnodeList="aum.20060511101147,aum.20060511113333,aum.20060511113333.1,aum.20060511114439,aum.20060511114439.1,aum.20060512150118,aum.20060511114439.2,aum.20060511120059,aum.20060511114604,aum.20060511114604.1,aum.20060511113333.3,aum.20060513071956,aum.20060511130507,aum.20060511120024"><vh>@nosent
 sitemgr.py</vh>
+<v t="aum.20060511101147" a="E" 
tnodeList="aum.20060511101147,aum.20060511113333,aum.20060511113333.1,aum.20060511114439,aum.20060511114439.1,aum.20060512150118,aum.20060511114439.2,aum.20060511120059,aum.20060511114604,aum.20060511114604.1,aum.20060511113333.3,aum.20060513071956,aum.20060507124316,aum.20060511130507,aum.20060511120024"><vh>@nosent
 sitemgr.py</vh>
 <v t="aum.20060511113333"><vh>imports</vh></v>
 <v t="aum.20060511113333.1"><vh>config</vh></v>
 <v t="aum.20060511114439" a="E"><vh>class SiteMgr</vh>
@@ -96,11 +101,17 @@
 <v t="aum.20060511114604.1"><vh>saveConfig</vh></v>
 <v t="aum.20060511113333.3"><vh>update</vh></v>
 <v t="aum.20060513071956"><vh>shutdown</vh></v>
+<v t="aum.20060507124316"><vh>_log</vh></v>
 </v>
 <v t="aum.20060511130507"><vh>help</vh></v>
 <v t="aum.20060511120024"><vh>mainline</vh></v>
 </v>
-<v t="aum.20060512140230" tnodeList="aum.20060512140230"><vh>@nosent 
updatesites.py</vh></v>
+<v t="aum.20060512140230" a="E" 
tnodeList="aum.20060512140230,aum.20060514132715,aum.20060514132715.1,aum.20060514132715.2,aum.20060514132715.3"><vh>@nosent
 updatesites.py</vh>
+<v t="aum.20060514132715"><vh>imports</vh></v>
+<v t="aum.20060514132715.1"><vh>globals</vh></v>
+<v t="aum.20060514132715.2"><vh>main</vh></v>
+<v t="aum.20060514132715.3"><vh>mainline</vh></v>
+</v>
 <v t="aum.20060513073239.3" tnodeList="aum.20060513073239.3"><vh>@nosent 
start.sh</vh></v>
 <v t="aum.20060513073239.4" tnodeList="aum.20060513073239.4"><vh>@nosent 
stop.sh</vh></v>
 </v>
@@ -372,14 +383,32 @@
     """
     Create a connection object

-    Arguments:
-        - clientName - name of client to use with reqs, defaults to random
+    Keyword Arguments:
+        - name - name of client to use with reqs, defaults to random. This
+          is crucial if you plan on making persistent requests
         - host - hostname, defaults to defaultFCPHost
         - port - port number, defaults to defaultFCPPort
         - logfile - a pathname or writable file object, to which log messages
           should be written, defaults to stdout
         - verbosity - how detailed the log messages should be, defaults to 0
           (silence)
+
+    Attributes of interest:
+        - jobs - a dict of currently running jobs (persistent and 
nonpersistent).
+          keys are job ids and values are JobTicket objects
+        - persistentJobs - a dict of persistent jobs from this session and
+          previous sessions
+          keys are job ids and values are JobTicket objects
+    
+    Notes:
+        - when the connection is created, a 'hello' handshake takes place.
+          After that handshake, the node sends back a list of outstanding 
persistent
+          requests left over from the last connection (based on the value of
+          the 'name' keyword passed into this constructor).
+          
+          This object then wraps all this info into JobTicket instances and 
stores
+          them in the self.persistentJobs dict
+                                                       
     """
     # grab and save parms
     self.name = kw.get('clientName', self._getUniqueId())
@@ -387,7 +416,7 @@
     self.port = kw.get('port', defaultFCPPort)

     # set up the logger
-    logfile = kw.get('logfile', sys.stderr)
+    logfile = kw.get('logfile', sys.stdout)
     if not hasattr(logfile, 'write'):
         # might be a pathname
         if not isinstance(logfile, str):
@@ -405,6 +434,7 @@

     # the pending job tickets
     self.jobs = {} # keyed by request ID
+    self.persistentJobs = {} # ditto

     # queue for incoming client requests
     self.clientReqQueue = Queue.Queue()
@@ -597,6 +627,10 @@
     Keywords:
         - async - whether to return immediately with a job ticket object, 
default
           False (wait for completion)
+        - persistence - default 'connection' - the kind of persistence for
+          this request. If 'reboot' or 'forever', this job will be able to
+          be recalled in subsequent FCP sessions
+
         - dsnly - whether to only check local datastore
         - ignoreds - don't check local datastore
         - file - if given, this is a pathname to which to store the retrieved 
key
@@ -616,12 +650,16 @@
     # format the request
     opts = {}

-    id = self._getUniqueId()
+    id = kw.pop("id", None)
+    if not id:
+        id = self._getUniqueId()

     opts['async'] = kw.pop('async', False)
     if kw.has_key('callback'):
         opts['callback'] = kw['callback']

+    opts['Persistence'] = kw.pop('persistence', 'connection')
+
     file = kw.pop("file", None)
     if file:
         opts['ReturnType'] = "disk"
@@ -676,7 +714,9 @@
                   - if status is 'failed' or 'pending', this will contain
                     a dict containing the response from node
     """
-    id = self._getUniqueId()
+    id = kw.pop("id", None)
+    if not id:
+        id = self._getUniqueId()

     return self._submitCmd(id, "GenerateSSK", Identifier=id, **kw)

@@ -760,11 +800,15 @@
         - mimetype - the mime type, default text/plain

     Keywords valid for all modes:
-        - maxretries - maximum number of retries, default 3
-        - priority - default 1
         - async - whether to do the job asynchronously, returning a job ticket
           object (default False)
+        - persistence - default 'connection' - the kind of persistence for
+          this request. If 'reboot' or 'forever', this job will be able to
+          be recalled in subsequent FCP sessions

+        - maxretries - maximum number of retries, default 3
+        - priority - default 1
+
     Notes:
         - exactly one of 'file', 'data' or 'dir' keyword arguments must be 
present
     """
@@ -782,10 +826,16 @@
     if kw.has_key('callback'):
         opts['callback'] = kw['callback']

+    opts['Persistence'] = kw.pop('persistence', 'connection')
+
     opts['URI'] = uri
     opts['Metadata.ContentType'] = kw.get("mimetype", "text/plain")
-    id = self._getUniqueId()
+
+    id = kw.pop("id", None)
+    if not id:
+        id = self._getUniqueId()
     opts['Identifier'] = id
+
     opts['Verbosity'] = 0
     opts['MaxRetries'] = kw.get("maxretries", 3)
     opts['PriorityClass'] = kw.get("priority", 1)
@@ -823,6 +873,7 @@
         return

     if not msg.endswith("\n"): msg += "\n"
+
     self.logfile.write(msg)
     self.logfile.flush()

@@ -6145,7 +6196,10 @@
         - maxretries - maximum number of retries, default 3
         - priority - default 1

-        - async - default False - if True, return a job ticket
+        - async - default False - if True, return immediately with a job ticket
+        - persistence - default 'connection' - the kind of persistence for
+          this request. If 'reboot' or 'forever', this job will be able to
+          be recalled in subsequent FCP sessions

     Returns:
         - the URI under which the freesite can be retrieved
@@ -6167,7 +6221,9 @@
     maxretries = kw.get('maxretries', 3)
     priority = kw.get('priority', 1)

-    id = self._getUniqueId()
+    id = kw.pop("id", None)
+    if not id:
+        id = self._getUniqueId()

     # derive final URI for insert
     uriFull = uri + sitename + "/"
@@ -6182,6 +6238,7 @@
                 "MaxRetries=%s" % maxretries,
                 "PriorityClass=%s" % priority,
                 "URI=%s" % uriFull,
+                "Persistence=%s" % kw.get("persistence", "connection"),
                 ]

     # scan directory and add its files
@@ -6221,6 +6278,7 @@
                            rawcmd=fullbuf,
                            async=kw.get('async', False),
                            callback=kw.get('callback', False),
+                           Persistence=kw.get('Persistence', 'connection'),
                            )

 </t>
@@ -6253,18 +6311,22 @@
     @others

 </t>
-<t tx="aum.20060511103841.1">def __init__(self, id, cmd, kw):
+<t tx="aum.20060511103841.1">def __init__(self, node, id, cmd, persistent, kw):
     """
     You should never instantiate a JobTicket object yourself
     """
+    self.node = node
     self.id = id
     self.cmd = cmd
+    self.isPersistent = persistent
+    self.kw = kw

+    self.msgs = []
+
     callback = kw.pop('callback', None)
     if callback:
         self.callback = callback

-    self.kw = kw

     self.lock = threading.Lock()
     self.lock.acquire()
@@ -6284,14 +6346,13 @@
     """
     self.lock.acquire()
     self.lock.release()
-    if isinstance(self.result, Exception):
-        raise self.result
-    else:
-        return self.result
-
+    return self.getResult()
 </t>
 <t tx="aum.20060511113333">import fcp, sys, os, sha, traceback

+# get log level constants
+from fcp import SILENT, FATAL, CRITICAL, ERROR, INFO, DETAIL, DEBUG
+
 from ConfigParser import SafeConfigParser

 </t>
@@ -6308,6 +6369,8 @@
     """
     noSites = True

+    log = self._log
+
     conf = self.config
     for sitename in conf.sections():
         uri = conf.get(sitename, "uri")
@@ -6322,8 +6385,8 @@
             h.update(f['hash'])
         hashNew = h.hexdigest()
         if hashNew != hash:
-            print "Updating site %s" % sitename
-            print "privatekey=%s" % privatekey
+            log(INFO, "Updating site %s" % sitename)
+            log(INFO, "privatekey=%s" % privatekey)
             noSites = False
             try:
                 res = self.node.put(privatekey,
@@ -6331,16 +6394,16 @@
                                     name=sitename,
                                     version=version,
                                     usk=True)
-                print "site %s updated successfully" % sitename
+                log(INFO, "site %s updated successfully" % sitename)
             except:
                 traceback.print_exc()
-                print "site %s failed to update" % sitename
+                log(ERROR, "site %s failed to update" % sitename)
             conf.set(sitename, "hash", hashNew)

     self.saveConfig()

     if noSites:
-        print "No sites needed updating"
+        log(INFO, "No sites needed updating")

 </t>
 <t tx="aum.20060511114439">class SiteMgr:
@@ -6357,9 +6420,23 @@
     Arguments:
         - configFile - ini-format file containing site specifications,
           defaults to ~/.freesitesrc on *nix or ~/freesites.ini
+
+    Keywords:
+        - logfile - a pathname or open file object to which to write
+          log messages, defaults to sys.stdout
     """
+    # set up the logger
+    logfile = kw.pop('logfile', sys.stderr)
+    if not hasattr(logfile, 'write'):
+        # might be a pathname
+        if not isinstance(logfile, str):
+            raise Exception("Bad logfile, must be pathname or file object")
+        logfile = file(logfile, "a")
+    self.logfile = logfile
+    self.verbosity = kw.get('verbosity', 0)
+
     # get a node handle
-    self.createNode(**kw)
+    self.createNode(logfile=logfile, **kw)

     # determine pathname for sites ini file
     if configFile == None:
@@ -6375,8 +6452,8 @@

     if not os.path.isfile(configFile):
         self.createConfig()
-        print "New config file created at %s"
-        print "Please edit that file and add your freesites"
+        self._log(CRITICAL, "New config file created at %s" % configFile)
+        self._log(CRITICAL, "Please edit that file and add your freesites")

     self.loadConfig()

@@ -6535,15 +6612,23 @@
     log = self._log

     # find the job this relates to
-    id = msg['Identifier']
+    id = msg.get('Identifier', '__global')
+
     hdr = msg['header']

     job = self.jobs.get(id, None)
-    
+
     # bail if job not known
     if not job:
-        log(ERROR, "Received %s for unknown job %s" % (hdr, id))
-        return
+        if hdr.startswith("Persistent"):
+            # we have a persistent job from last connection
+            log(INFO, "Got %s from prior session" % hdr)
+            job = JobTicket(self, id, hdr, True, msg)
+            self.jobs[id] = job
+            self.persistentJobs[id] = job
+        else:
+            log(ERROR, "Received %s for unknown job %s" % (hdr, id))
+            return

     # action from here depends on what kind of message we got

@@ -6564,6 +6649,7 @@
     # handle ClientGet responses

     if hdr == 'DataFound':
+        log(INFO, "Got DataFound for URI=%s" % job.kw['URI'])
         mimetype = msg['Metadata.ContentType']
         if job.kw.has_key('Filename'):
             # already stored to disk, done
@@ -6580,9 +6666,21 @@
             job._putResult(result)
             del self.jobs[id]
             return
-
+        
         # otherwise, we're expecting an AllData and will react to it then
         else:
+            # is this a persistent get?
+            if job.kw['ReturnType'] == 'direct' \
+            and job.kw['Persistence'] != 'connection':
+                # gotta poll for request status so we can get our data
+                # FIXME: this is a hack, clean it up
+                log(INFO, "Request was persistent")
+                if not hasattr(job, "gotPersistentDataFound"):
+                    job.gotPersistentDataFound = True
+                    log(INFO, "  --&gt; sending GetRequestStatus")
+                    self._txMsg("GetRequestStatus",
+                                Identifier=job.kw['Identifier'])
+
             job.callback('pending', msg)
             job.mimetype = mimetype
             return
@@ -6652,18 +6750,24 @@

     if hdr == 'PersistentGet':
         job.callback('pending', msg)
+        job._appendMsg(msg)
         return

     if hdr == 'PersistentPut':
         job.callback('pending', msg)
+        job._appendMsg(msg)
         return

-    if hdr == 'EndListPersistentRequests':
+    if hdr == 'PersistentPutDir':
         job.callback('pending', msg)
+        job._appendMsg(msg)
         return

-    if hdr == 'PersistentPutDir':
-        job.callback('pending', msg)
+    if hdr == 'EndListPersistentRequests':
+        job._appendMsg(msg)
+        job.callback('successful', job.msgs)
+        job._putResult(job.msgs)
+        del self.jobs[job.id]
         return

     # -----------------------------
@@ -6690,20 +6794,21 @@
     job._putResult(FCPException(msg))
     del self.jobs[id]
     return
-
 </t>
-<t tx="aum.20060511205201.2">def _on_clientReq(self, req):
+<t tx="aum.20060511205201.2">def _on_clientReq(self, job):
     """
-    takes an incoming requests from client and transmits it to
+    takes an incoming request job from client and transmits it to
     the fcp port, and also registers it so the manager thread
     can action responses from the fcp port.
     """
-    id = req.id
-    cmd = req.cmd
-    kw = req.kw
+    id = job.id
+    cmd = job.cmd
+    kw = job.kw

     # register the req
-    self.jobs[id] = req
+    self.jobs[id] = job
+    if job.kw.get("Persistence", "connection") != "connection":
+        self.persistentJobs[id] = job

     # now can send, since we're the only one who will
     self._txMsg(cmd, **kw)
@@ -6740,9 +6845,9 @@
           object which the client can poll or block on later
     """
     async = kw.pop('async', False)
+    persistent = kw.get("Persistence", "connection") != "connection"
+    job = JobTicket(self, id, cmd, persistent, kw)

-    job = JobTicket(id, cmd, kw)
-
     self.clientReqQueue.put(job)

     self._log(DEBUG, "_submitCmd: id=%s cmd=%s kw=%s" % (id, cmd, 
str(kw)[:256]))
@@ -6763,68 +6868,10 @@

 </t>
 <t tx="aum.20060512140230">@first #!/usr/bin/env python
-
-import sys, os, time, commands
-import sitemgr
-
-# time we wait after starting fred, to allow the node to 'warm up'
-# and make connections to its peers
-startupTime = 180
-
-# directory where we have freenet installed,
-# change it as needed
-freenetDir = "/home/david/freenet"
-
-# derive path of freenet pid file, the (non)existence
-# of which is the easiest test of whether the freenet
-# node is running
-pidFile = os.path.join(freenetDir, "Freenet.pid")
-
-# small wrapper which, if freenet isn't already running,
-# starts it prior to inserting then stops it after
-# inserting
-def main(verbose=None):
-
-    if verbose == None:
-        verbose = ('-v' in sys.argv)
-
-    print "--------------------------------------------"
-    print "Start of site updating run"
-    
-    # start freenet and let it warm up, if it's not already running
-    if not os.path.isfile(pidFile):
-        startingFreenet = True
-        os.chdir(freenetDir)
-        print "Starting freenet..."
-        print os.system("%s/start.sh &amp;" % freenetDir)
-        print "Letting node settle for %s seconds..." % startupTime
-        time.sleep(startupTime)
-    else:
-        print "Freenet node is already running!"
-        startingFreenet = False
-
-    # add verbosity argument if needed    
-    if verbose:
-        kw = {"verbosity" : sitemgr.fcp.DETAIL}
-    else:
-        kw = {}
-
-    # get a site manager object, and perform the actual insertions
-    s = sitemgr.SiteMgr(**kw)
-    s.update()
-    del s
-    
-    # kill freenet if it was dynamically started
-    if startingFreenet:
-        print "Waiting %s for inserts to finish..." % startupTime
-        time.sleep(startupTime)
-        print "Stopping node..."
-        os.system("./run.sh stop")
-        print "Node stopped"
-
-if __name__ == '__main__':
-    main()
-
+"""
+A utility to update freesites from within a cron environment
+"""
+ at others
 </t>
 <t tx="aum.20060512150118">def __del__(self):

@@ -7111,5 +7158,167 @@
 sh("cp -r %s %s" % (tarball, freesiteDir))

 </t>
+<t tx="aum.20060514124642">def refreshPersistentRequests(self, **kw):
+    """
+    Sends a ListPersistentRequests to node, to ensure that
+    our records of persistent requests are up to date.
+    
+    Since, upon connection, the node sends us a list of all
+    outstanding persistent requests anyway, I can't really
+    see much use for this method. I've only added the method
+    for FCP spec compliance
+    """
+    self._log(INFO, "listPersistentRequests")
+
+    if self.jobs.has_key('__global'):
+        raise Exception("An existing non-identifier job is currently pending")
+
+    # ---------------------------------
+    # format the request
+    opts = {}
+
+    id = '__global'
+    opts['Identifier'] = id
+
+    opts['async'] = kw.pop('async', False)
+    if kw.has_key('callback'):
+        opts['callback'] = kw['callback']
+
+    # ---------------------------------
+    # now enqueue the request
+    return self._submitCmd(id, "ListPersistentRequests", **opts)
+
+</t>
+<t tx="aum.20060514124934">def _appendMsg(self, msg):
+    self.msgs.append(msg)
+
+</t>
+<t tx="aum.20060514132715">import sys, os, time, commands
+import sitemgr
+
+</t>
+<t tx="aum.20060514132715.1"># time we wait after starting fred, to allow the 
node to 'warm up'
+# and make connections to its peers
+startupTime = 180
+
+# directory where we have freenet installed,
+# change it as needed
+freenetDir = "/home/david/freenet"
+
+# derive path of freenet pid file, the (non)existence
+# of which is the easiest test of whether the freenet
+# node is running
+freenetPidFile = os.path.join(freenetDir, "Freenet.pid")
+
+logFile = os.path.join(freenetDir, "updatesites.log")
+pidFile = os.path.join(freenetDir, "updatesites.pid")
+
+</t>
+<t tx="aum.20060514132715.2"># small wrapper which, if freenet isn't already 
running,
+# starts it prior to inserting then stops it after
+# inserting
+def main(verbose=None):
+
+    if verbose == None:
+        verbose = ('-v' in sys.argv)
+
+    if os.path.isfile(pidFile):
+        print "updatesites.py already running: pid=%s" % file(pidFile).read()
+        sys.exit(1)
+    f = file(pidFile, "w")
+    f.write(str(os.getpid()))
+    f.close()
+
+    try:
+        print "--------------------------------------------"
+        print "Start of site updating run"
+        
+        # start freenet and let it warm up, if it's not already running
+        if not os.path.isfile(freenetPidFile):
+            startingFreenet = True
+            os.chdir(freenetDir)
+            print "Starting freenet..."
+            print os.system("%s/start.sh &amp;" % freenetDir)
+            print "Letting node settle for %s seconds..." % startupTime
+            time.sleep(startupTime)
+        else:
+            print "Freenet node is already running!"
+            startingFreenet = False
+    
+        # add verbosity argument if needed    
+        if verbose:
+            kw = {"verbosity" : sitemgr.fcp.DETAIL}
+        else:
+            kw = {"verbosity" : sitemgr.fcp.INFO}
+    
+        # get a site manager object, and perform the actual insertions
+        s = sitemgr.SiteMgr(**kw)
+        s.update()
+        del s
+        
+        # kill freenet if it was dynamically started
+        if startingFreenet:
+            print "Waiting %s for inserts to finish..." % startupTime
+            time.sleep(startupTime)
+            print "Stopping node..."
+            os.system("./run.sh stop")
+            print "Node stopped"
+    except:
+        pass
+
+    # can now drop our pidfile
+    os.unlink(pidFile)
+
+</t>
+<t tx="aum.20060514132715.3">if __name__ == '__main__':
+    main()
+
+</t>
+<t tx="aum.20060514134235">def getResult(self):
+    """
+    Returns result of job, or None if job still not complete
+
+    If result is an exception object, then raises it
+    """
+    if isinstance(self.result, Exception):
+        raise self.result
+    else:
+        return self.result
+
+</t>
+<t tx="aum.20060514162944">def cancel(self):
+    """
+    Cancels the job, if it is persistent
+    
+    Does nothing if the job was not persistent
+    """
+    if not self.isPersistent:
+        return
+
+    # remove from node's jobs lists
+    try:
+        del self.node.jobs[self.id]
+    except:
+        pass
+    if self.isPersistent:
+        try:
+            del self.node.persistentJobs[self.id]
+        except:
+            pass
+    
+    # send the cancel
+    self.node._txMsg("RemovePersistentRequest",
+                     Global="false",
+                     Identifier=self.id)
+
+</t>
+<t tx="aum.20060514164052">def __repr__(self):
+    if self.kw.has_key("URI"):
+        uri = " URI=%s" % self.kw['URI']
+    else:
+        uri = ""
+    return "&lt;FCP job %s:%s%s" % (self.id, self.cmd, uri)
+
+</t>
 </tnodes>
 </leo_file>

Modified: trunk/apps/pyFreenet/fcp.py
===================================================================
--- trunk/apps/pyFreenet/fcp.py 2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/fcp.py 2006-05-14 05:23:40 UTC (rev 8697)
@@ -122,14 +122,32 @@
         """
         Create a connection object

-        Arguments:
-            - clientName - name of client to use with reqs, defaults to random
+        Keyword Arguments:
+            - name - name of client to use with reqs, defaults to random. This
+              is crucial if you plan on making persistent requests
             - host - hostname, defaults to defaultFCPHost
             - port - port number, defaults to defaultFCPPort
             - logfile - a pathname or writable file object, to which log 
messages
               should be written, defaults to stdout
             - verbosity - how detailed the log messages should be, defaults to 0
               (silence)
+    
+        Attributes of interest:
+            - jobs - a dict of currently running jobs (persistent and 
nonpersistent).
+              keys are job ids and values are JobTicket objects
+            - persistentJobs - a dict of persistent jobs from this session and
+              previous sessions
+              keys are job ids and values are JobTicket objects
+        
+        Notes:
+            - when the connection is created, a 'hello' handshake takes place.
+              After that handshake, the node sends back a list of outstanding 
persistent
+              requests left over from the last connection (based on the value 
of
+              the 'name' keyword passed into this constructor).
+              
+              This object then wraps all this info into JobTicket instances 
and stores
+              them in the self.persistentJobs dict
+                                                           
         """
         # grab and save parms
         self.name = kw.get('clientName', self._getUniqueId())
@@ -137,7 +155,7 @@
         self.port = kw.get('port', defaultFCPPort)

         # set up the logger
-        logfile = kw.get('logfile', sys.stderr)
+        logfile = kw.get('logfile', sys.stdout)
         if not hasattr(logfile, 'write'):
             # might be a pathname
             if not isinstance(logfile, str):
@@ -155,6 +173,7 @@

         # the pending job tickets
         self.jobs = {} # keyed by request ID
+        self.persistentJobs = {} # ditto

         # queue for incoming client requests
         self.clientReqQueue = Queue.Queue()
@@ -192,7 +211,9 @@
                       - if status is 'failed' or 'pending', this will contain
                         a dict containing the response from node
         """
-        id = self._getUniqueId()
+        id = kw.pop("id", None)
+        if not id:
+            id = self._getUniqueId()

         return self._submitCmd(id, "GenerateSSK", Identifier=id, **kw)

@@ -203,6 +224,10 @@
         Keywords:
             - async - whether to return immediately with a job ticket object, 
default
               False (wait for completion)
+            - persistence - default 'connection' - the kind of persistence for
+              this request. If 'reboot' or 'forever', this job will be able to
+              be recalled in subsequent FCP sessions
+    
             - dsnly - whether to only check local datastore
             - ignoreds - don't check local datastore
             - file - if given, this is a pathname to which to store the 
retrieved key
@@ -222,12 +247,16 @@
         # format the request
         opts = {}

-        id = self._getUniqueId()
+        id = kw.pop("id", None)
+        if not id:
+            id = self._getUniqueId()

         opts['async'] = kw.pop('async', False)
         if kw.has_key('callback'):
             opts['callback'] = kw['callback']

+        opts['Persistence'] = kw.pop('persistence', 'connection')
+    
         file = kw.pop("file", None)
         if file:
             opts['ReturnType'] = "disk"
@@ -291,11 +320,15 @@
             - mimetype - the mime type, default text/plain

         Keywords valid for all modes:
-            - maxretries - maximum number of retries, default 3
-            - priority - default 1
             - async - whether to do the job asynchronously, returning a job 
ticket
               object (default False)
+            - persistence - default 'connection' - the kind of persistence for
+              this request. If 'reboot' or 'forever', this job will be able to
+              be recalled in subsequent FCP sessions

+            - maxretries - maximum number of retries, default 3
+            - priority - default 1
+    
         Notes:
             - exactly one of 'file', 'data' or 'dir' keyword arguments must be 
present
         """
@@ -313,10 +346,16 @@
         if kw.has_key('callback'):
             opts['callback'] = kw['callback']

+        opts['Persistence'] = kw.pop('persistence', 'connection')
+    
         opts['URI'] = uri
         opts['Metadata.ContentType'] = kw.get("mimetype", "text/plain")
-        id = self._getUniqueId()
+    
+        id = kw.pop("id", None)
+        if not id:
+            id = self._getUniqueId()
         opts['Identifier'] = id
+    
         opts['Verbosity'] = 0
         opts['MaxRetries'] = kw.get("maxretries", 3)
         opts['PriorityClass'] = kw.get("priority", 1)
@@ -362,7 +401,10 @@
             - maxretries - maximum number of retries, default 3
             - priority - default 1

-            - async - default False - if True, return a job ticket
+            - async - default False - if True, return immediately with a job 
ticket
+            - persistence - default 'connection' - the kind of persistence for
+              this request. If 'reboot' or 'forever', this job will be able to
+              be recalled in subsequent FCP sessions

         Returns:
             - the URI under which the freesite can be retrieved
@@ -384,7 +426,9 @@
         maxretries = kw.get('maxretries', 3)
         priority = kw.get('priority', 1)

-        id = self._getUniqueId()
+        id = kw.pop("id", None)
+        if not id:
+            id = self._getUniqueId()

         # derive final URI for insert
         uriFull = uri + sitename + "/"
@@ -399,6 +443,7 @@
                     "MaxRetries=%s" % maxretries,
                     "PriorityClass=%s" % priority,
                     "URI=%s" % uriFull,
+                    "Persistence=%s" % kw.get("persistence", "connection"),
                     ]

         # scan directory and add its files
@@ -438,8 +483,39 @@
                                rawcmd=fullbuf,
                                async=kw.get('async', False),
                                callback=kw.get('callback', False),
+                               Persistence=kw.get('Persistence', 'connection'),
                                )

+    def refreshPersistentRequests(self, **kw):
+        """
+        Sends a ListPersistentRequests to node, to ensure that
+        our records of persistent requests are up to date.
+        
+        Since, upon connection, the node sends us a list of all
+        outstanding persistent requests anyway, I can't really
+        see much use for this method. I've only added the method
+        for FCP spec compliance
+        """
+        self._log(INFO, "listPersistentRequests")
+    
+        if self.jobs.has_key('__global'):
+            raise Exception("An existing non-identifier job is currently 
pending")
+    
+        # ---------------------------------
+        # format the request
+        opts = {}
+    
+        id = '__global'
+        opts['Identifier'] = id
+    
+        opts['async'] = kw.pop('async', False)
+        if kw.has_key('callback'):
+            opts['callback'] = kw['callback']
+    
+        # ---------------------------------
+        # now enqueue the request
+        return self._submitCmd(id, "ListPersistentRequests", **opts)
+    
     def shutdown(self):
         """
         Terminates the manager thread
@@ -536,9 +612,9 @@
               object which the client can poll or block on later
         """
         async = kw.pop('async', False)
+        persistent = kw.get("Persistence", "connection") != "connection"
+        job = JobTicket(self, id, cmd, persistent, kw)

-        job = JobTicket(id, cmd, kw)
-    
         self.clientReqQueue.put(job)

         self._log(DEBUG, "_submitCmd: id=%s cmd=%s kw=%s" % (id, cmd, 
str(kw)[:256]))
@@ -558,15 +634,23 @@
         log = self._log

         # find the job this relates to
-        id = msg['Identifier']
+        id = msg.get('Identifier', '__global')
+    
         hdr = msg['header']

         job = self.jobs.get(id, None)
-        
+    
         # bail if job not known
         if not job:
-            log(ERROR, "Received %s for unknown job %s" % (hdr, id))
-            return
+            if hdr.startswith("Persistent"):
+                # we have a persistent job from last connection
+                log(INFO, "Got %s from prior session" % hdr)
+                job = JobTicket(self, id, hdr, True, msg)
+                self.jobs[id] = job
+                self.persistentJobs[id] = job
+            else:
+                log(ERROR, "Received %s for unknown job %s" % (hdr, id))
+                return

         # action from here depends on what kind of message we got

@@ -587,6 +671,7 @@
         # handle ClientGet responses

         if hdr == 'DataFound':
+            log(INFO, "Got DataFound for URI=%s" % job.kw['URI'])
             mimetype = msg['Metadata.ContentType']
             if job.kw.has_key('Filename'):
                 # already stored to disk, done
@@ -603,9 +688,21 @@
                 job._putResult(result)
                 del self.jobs[id]
                 return
-    
+            
             # otherwise, we're expecting an AllData and will react to it then
             else:
+                # is this a persistent get?
+                if job.kw['ReturnType'] == 'direct' \
+                and job.kw['Persistence'] != 'connection':
+                    # gotta poll for request status so we can get our data
+                    # FIXME: this is a hack, clean it up
+                    log(INFO, "Request was persistent")
+                    if not hasattr(job, "gotPersistentDataFound"):
+                        job.gotPersistentDataFound = True
+                        log(INFO, "  --> sending GetRequestStatus")
+                        self._txMsg("GetRequestStatus",
+                                    Identifier=job.kw['Identifier'])
+    
                 job.callback('pending', msg)
                 job.mimetype = mimetype
                 return
@@ -675,18 +772,24 @@

         if hdr == 'PersistentGet':
             job.callback('pending', msg)
+            job._appendMsg(msg)
             return

         if hdr == 'PersistentPut':
             job.callback('pending', msg)
+            job._appendMsg(msg)
             return

-        if hdr == 'EndListPersistentRequests':
+        if hdr == 'PersistentPutDir':
             job.callback('pending', msg)
+            job._appendMsg(msg)
             return

-        if hdr == 'PersistentPutDir':
-            job.callback('pending', msg)
+        if hdr == 'EndListPersistentRequests':
+            job._appendMsg(msg)
+            job.callback('successful', job.msgs)
+            job._putResult(job.msgs)
+            del self.jobs[job.id]
             return

         # -----------------------------
@@ -713,19 +816,20 @@
         job._putResult(FCPException(msg))
         del self.jobs[id]
         return
-    
-    def _on_clientReq(self, req):
+    def _on_clientReq(self, job):
         """
-        takes an incoming requests from client and transmits it to
+        takes an incoming request job from client and transmits it to
         the fcp port, and also registers it so the manager thread
         can action responses from the fcp port.
         """
-        id = req.id
-        cmd = req.cmd
-        kw = req.kw
+        id = job.id
+        cmd = job.cmd
+        kw = job.kw

         # register the req
-        self.jobs[id] = req
+        self.jobs[id] = job
+        if job.kw.get("Persistence", "connection") != "connection":
+            self.persistentJobs[id] = job

         # now can send, since we're the only one who will
         self._txMsg(cmd, **kw)
@@ -892,6 +996,7 @@
             return

         if not msg.endswith("\n"): msg += "\n"
+    
         self.logfile.write(msg)
         self.logfile.flush()

@@ -907,18 +1012,22 @@
         - poll the job for completion status
         - receive a callback upon completion
     """
-    def __init__(self, id, cmd, kw):
+    def __init__(self, node, id, cmd, persistent, kw):
         """
         You should never instantiate a JobTicket object yourself
         """
+        self.node = node
         self.id = id
         self.cmd = cmd
+        self.isPersistent = persistent
+        self.kw = kw

+        self.msgs = []
+    
         callback = kw.pop('callback', None)
         if callback:
             self.callback = callback

-        self.kw = kw

         self.lock = threading.Lock()
         self.lock.acquire()
@@ -936,6 +1045,13 @@
         """
         self.lock.acquire()
         self.lock.release()
+        return self.getResult()
+    def getResult(self):
+        """
+        Returns result of job, or None if job still not complete
+    
+        If result is an exception object, then raises it
+        """
         if isinstance(self.result, Exception):
             raise self.result
         else:
@@ -948,6 +1064,34 @@
         """
         # no action needed

+    def cancel(self):
+        """
+        Cancels the job, if it is persistent
+        
+        Does nothing if the job was not persistent
+        """
+        if not self.isPersistent:
+            return
+    
+        # remove from node's jobs lists
+        try:
+            del self.node.jobs[self.id]
+        except:
+            pass
+        if self.isPersistent:
+            try:
+                del self.node.persistentJobs[self.id]
+            except:
+                pass
+        
+        # send the cancel
+        self.node._txMsg("RemovePersistentRequest",
+                         Global="false",
+                         Identifier=self.id)
+    
+    def _appendMsg(self, msg):
+        self.msgs.append(msg)
+    
     def _putResult(self, result):
         """
         Called by manager thread to indicate job is complete,
@@ -956,6 +1100,13 @@
         self.result = result
         self.lock.release()

+    def __repr__(self):
+        if self.kw.has_key("URI"):
+            uri = " URI=%s" % self.kw['URI']
+        else:
+            uri = ""
+        return "<FCP job %s:%s%s" % (self.id, self.cmd, uri)
+    

 def toBool(arg):
     try:

Modified: 
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
===================================================================
--- 
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
      2006-05-13 21:59:31 UTC (rev 8696)
+++ 
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
      2006-05-14 05:23:40 UTC (rev 8697)
@@ -309,7 +309,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: 
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
===================================================================
--- 
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
  2006-05-13 21:59:31 UTC (rev 8696)
+++ 
trunk/apps/pyFreenet/html/private/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
  2006-05-14 05:23:40 UTC (rev 8697)
@@ -232,7 +232,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/SocketServer.BaseServer-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/SocketServer.BaseServer-class.html        
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/SocketServer.BaseServer-class.html        
2006-05-14 05:23:40 UTC (rev 8697)
@@ -278,7 +278,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/SocketServer.TCPServer-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/SocketServer.TCPServer-class.html 
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/SocketServer.TCPServer-class.html 
2006-05-14 05:23:40 UTC (rev 8697)
@@ -382,7 +382,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: 
trunk/apps/pyFreenet/html/private/SocketServer.ThreadingMixIn-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/SocketServer.ThreadingMixIn-class.html    
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/SocketServer.ThreadingMixIn-class.html    
2006-05-14 05:23:40 UTC (rev 8697)
@@ -157,7 +157,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/exceptions.Exception-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/exceptions.Exception-class.html   
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/exceptions.Exception-class.html   
2006-05-14 05:23:40 UTC (rev 8697)
@@ -86,7 +86,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp-module.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp-module.html   2006-05-13 21:59:31 UTC 
(rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp-module.html   2006-05-14 05:23:40 UTC 
(rev 8697)
@@ -458,7 +458,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp.ConnectionRefused-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp.ConnectionRefused-class.html  
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp.ConnectionRefused-class.html  
2006-05-14 05:23:40 UTC (rev 8697)
@@ -89,7 +89,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp.FCPException-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp.FCPException-class.html       
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp.FCPException-class.html       
2006-05-14 05:23:40 UTC (rev 8697)
@@ -93,7 +93,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp.FCPGetFailed-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp.FCPGetFailed-class.html       
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp.FCPGetFailed-class.html       
2006-05-14 05:23:40 UTC (rev 8697)
@@ -91,7 +91,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp.FCPNodeConnection-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp.FCPNodeConnection-class.html  
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp.FCPNodeConnection-class.html  
2006-05-14 05:23:40 UTC (rev 8697)
@@ -132,6 +132,12 @@
 <br />
 Inserts a freesite</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><span class="summary-sig"><a 
href="fcp.FCPNodeConnection-class.html#refreshPersistentRequests" 
class="summary-sig-name"><code>refreshPersistentRequests</code></a>(<span 
class=summary-sig-arg>self</span>,
+          <span class="summary-sig-kwarg">**kw</span>)</span></code>
+<br />
+Sends a ListPersistentRequests to node, to ensure that our records of 
+persistent requests are up to date.</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.FCPNodeConnection-class.html#shutdown" 
class="summary-sig-name"><code>shutdown</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
 <br />
 Terminates the manager thread</td></tr>
@@ -160,9 +166,9 @@
 Returns True if a message is coming in from the node</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="../private/fcp.FCPNodeConnection-class.html#_on_clientReq" 
class="summary-sig-name"><code>_on_clientReq</code></a>(<span 
class=summary-sig-arg>self</span>,
-          <span class=summary-sig-arg>req</span>)</span></code>
+          <span class=summary-sig-arg>job</span>)</span></code>
 <br />
-takes an incoming requests from client and transmits it to the fcp 
+takes an incoming request job from client and transmits it to the fcp 
 port, and also registers it so the manager thread can action responses 
 from the fcp port.</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
@@ -203,11 +209,11 @@
     <br /><i>(Constructor)</i>
   </h3>
   <p>Create a connection object</p>
-  Arguments:
+  Keyword Arguments:
   <ul>
     <li>
-      clientName - name of client to use with reqs, defaults to 
-      random
+      name - name of client to use with reqs, defaults to random. This 
+      is crucial if you plan on making persistent requests
     </li>
     <li>
       host - hostname, defaults to defaultFCPHost
@@ -224,6 +230,30 @@
       0 (silence)
     </li>
   </ul>
+  Attributes of interest:
+  <ul>
+    <li>
+      jobs - a dict of currently running jobs (persistent and 
+      nonpersistent). keys are job ids and values are JobTicket 
+      objects
+    </li>
+    <li>
+      persistentJobs - a dict of persistent jobs from this session and 
+      previous sessions keys are job ids and values are JobTicket 
+      objects
+    </li>
+  </ul>
+  Notes:
+  <ul>
+    <li>
+      <p>when the connection is created, a 'hello' handshake takes place. 
+      After that handshake, the node sends back a list of outstanding 
+      persistent requests left over from the last connection (based on 
+      the value of the 'name' keyword passed into this constructor).</p>
+      This object then wraps all this info into JobTicket instances 
+      and stores them in the self.persistentJobs dict
+    </li>
+  </ul>
   <dl><dt></dt><dd>
   </dd></dl>
 </td></tr></table>
@@ -292,6 +322,11 @@
       default False (wait for completion)
     </li>
     <li>
+      persistence - default 'connection' - the kind of persistence for 
+      this request. If 'reboot' or 'forever', this job will be able to be 
+      recalled in subsequent FCP sessions
+    </li>
+    <li>
       dsnly - whether to only check local datastore
     </li>
     <li>
@@ -388,15 +423,20 @@
   Keywords valid for all modes:
   <ul>
     <li>
+      async - whether to do the job asynchronously, returning a job 
+      ticket object (default False)
+    </li>
+    <li>
+      persistence - default 'connection' - the kind of persistence for 
+      this request. If 'reboot' or 'forever', this job will be able to be 
+      recalled in subsequent FCP sessions
+    </li>
+    <li>
       maxretries - maximum number of retries, default 3
     </li>
     <li>
       priority - default 1
     </li>
-    <li>
-      async - whether to do the job asynchronously, returning a job 
-      ticket object (default False)
-    </li>
   </ul>
   Notes:
   <ul>
@@ -444,8 +484,14 @@
       priority - default 1
     </li>
     <li>
-      async - default False - if True, return a job ticket
+      async - default False - if True, return immediately with a job 
+      ticket
     </li>
+    <li>
+      persistence - default 'connection' - the kind of persistence for 
+      this request. If 'reboot' or 'forever', this job will be able to be 
+      recalled in subsequent FCP sessions
+    </li>
   </ul>
   Returns:
   <ul>
@@ -457,6 +503,20 @@
   </dd></dl>
 </td></tr></table>

+<a name="refreshPersistentRequests"></a>
+<table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
+  <h3><span class="sig"><span 
class="sig-name">refreshPersistentRequests</span>(<span 
class=sig-arg>self</span>,
+          <span class="sig-kwarg">**kw</span>)</span>
+  </h3>
+  <p>Sends a ListPersistentRequests to node, to ensure that our records 
+  of persistent requests are up to date.</p>
+  Since, upon connection, the node sends us a list of all outstanding 
+  persistent requests anyway, I can't really see much use for this 
+  method. I've only added the method for FCP spec compliance
+  <dl><dt></dt><dd>
+  </dd></dl>
+</td></tr></table>
+
 <a name="shutdown"></a>
 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
   <h3><span class="sig"><span class="sig-name">shutdown</span>(<span 
class=sig-arg>self</span>)</span>
@@ -519,11 +579,11 @@
 <a name="_on_clientReq"></a>
 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
   <h3><span class="sig"><span class="sig-name">_on_clientReq</span>(<span 
class=sig-arg>self</span>,
-          <span class=sig-arg>req</span>)</span>
+          <span class=sig-arg>job</span>)</span>
   </h3>
-  takes an incoming requests from client and transmits it to the fcp 
-  port, and also registers it so the manager thread can action responses 
-  from the fcp port.
+  takes an incoming request job from client and transmits it to the 
+  fcp port, and also registers it so the manager thread can action 
+  responses from the fcp port.
   <dl><dt></dt><dd>
   </dd></dl>
 </td></tr></table>
@@ -650,7 +710,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp.FCPProtocolError-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp.FCPProtocolError-class.html   
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp.FCPProtocolError-class.html   
2006-05-14 05:23:40 UTC (rev 8697)
@@ -91,7 +91,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp.FCPPutFailed-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp.FCPPutFailed-class.html       
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp.FCPPutFailed-class.html       
2006-05-14 05:23:40 UTC (rev 8697)
@@ -91,7 +91,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcp.JobTicket-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcp.JobTicket-class.html  2006-05-13 
21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcp.JobTicket-class.html  2006-05-14 
05:23:40 UTC (rev 8697)
@@ -67,12 +67,17 @@
   <th colspan="2">Method Summary</th></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#__init__" 
class="summary-sig-name"><code>__init__</code></a>(<span 
class=summary-sig-arg>self</span>,
+          <span class=summary-sig-arg>node</span>,
           <span class=summary-sig-arg>id</span>,
           <span class=summary-sig-arg>cmd</span>,
+          <span class=summary-sig-arg>persistent</span>,
           <span class=summary-sig-arg>kw</span>)</span></code>
 <br />
 You should never instantiate a JobTicket object yourself</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><a name="__repr__"></a><span class="summary-sig"><span 
class="summary-sig-name">__repr__</span>(<span 
class=summary-sig-arg>self</span>)</span></code>
+</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#callback" 
class="summary-sig-name"><code>callback</code></a>(<span 
class=summary-sig-arg>self</span>,
           <span class=summary-sig-arg>status</span>,
           <span class=summary-sig-arg>value</span>)</span></code>
@@ -80,6 +85,14 @@
 This will be replaced in job ticket instances wherever user provides 
 callback arguments</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#cancel" 
class="summary-sig-name"><code>cancel</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
+<br />
+Cancels the job, if it is persistent</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#getResult" 
class="summary-sig-name"><code>getResult</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
+<br />
+Returns result of job, or None if job still not complete</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#isComplete" 
class="summary-sig-name"><code>isComplete</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
 <br />
 Returns True if the job has been completed</td></tr>
@@ -89,6 +102,10 @@
 <br />
 Waits forever (or for a given timeout) for a job to complete</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><a name="_appendMsg"></a><span class="summary-sig"><span 
class="summary-sig-name">_appendMsg</span>(<span 
class=summary-sig-arg>self</span>,
+          <span class=summary-sig-arg>msg</span>)</span></code>
+</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="../private/fcp.JobTicket-class.html#_putResult" 
class="summary-sig-name"><code>_putResult</code></a>(<span 
class=summary-sig-arg>self</span>,
           <span class=summary-sig-arg>result</span>)</span></code>
 <br />
@@ -106,8 +123,10 @@
 <a name="__init__"></a>
 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
   <h3><span class="sig"><span class="sig-name">__init__</span>(<span 
class=sig-arg>self</span>,
+          <span class=sig-arg>node</span>,
           <span class=sig-arg>id</span>,
           <span class=sig-arg>cmd</span>,
+          <span class=sig-arg>persistent</span>,
           <span class=sig-arg>kw</span>)</span>
     <br /><i>(Constructor)</i>
   </h3>
@@ -128,6 +147,26 @@
   </dd></dl>
 </td></tr></table>

+<a name="cancel"></a>
+<table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
+  <h3><span class="sig"><span class="sig-name">cancel</span>(<span 
class=sig-arg>self</span>)</span>
+  </h3>
+  <p>Cancels the job, if it is persistent</p>
+  Does nothing if the job was not persistent
+  <dl><dt></dt><dd>
+  </dd></dl>
+</td></tr></table>
+
+<a name="getResult"></a>
+<table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
+  <h3><span class="sig"><span class="sig-name">getResult</span>(<span 
class=sig-arg>self</span>)</span>
+  </h3>
+  <p>Returns result of job, or None if job still not complete</p>
+  If result is an exception object, then raises it
+  <dl><dt></dt><dd>
+  </dd></dl>
+</td></tr></table>
+
 <a name="isComplete"></a>
 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
   <h3><span class="sig"><span class="sig-name">isComplete</span>(<span 
class=sig-arg>self</span>)</span>
@@ -178,7 +217,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcpxmlrpc-module.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcpxmlrpc-module.html     2006-05-13 
21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcpxmlrpc-module.html     2006-05-14 
05:23:40 UTC (rev 8697)
@@ -185,7 +185,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/fcpxmlrpc.FCPXMLRPCServer-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/fcpxmlrpc.FCPXMLRPCServer-class.html      
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/fcpxmlrpc.FCPXMLRPCServer-class.html      
2006-05-14 05:23:40 UTC (rev 8697)
@@ -294,7 +294,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: 
trunk/apps/pyFreenet/html/private/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
===================================================================
--- 
trunk/apps/pyFreenet/html/private/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
  2006-05-13 21:59:31 UTC (rev 8696)
+++ 
trunk/apps/pyFreenet/html/private/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
  2006-05-14 05:23:40 UTC (rev 8697)
@@ -142,7 +142,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/help.html
===================================================================
--- trunk/apps/pyFreenet/html/private/help.html 2006-05-13 21:59:31 UTC (rev 
8696)
+++ trunk/apps/pyFreenet/html/private/help.html 2006-05-14 05:23:40 UTC (rev 
8697)
@@ -225,7 +225,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/indices.html
===================================================================
--- trunk/apps/pyFreenet/html/private/indices.html      2006-05-13 21:59:31 UTC 
(rev 8696)
+++ trunk/apps/pyFreenet/html/private/indices.html      2006-05-14 05:23:40 UTC 
(rev 8697)
@@ -67,10 +67,14 @@
     <td>Method in class <a 
href="fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html"><code>fcpxmlrpc.FreenetXMLRPCRequestHandler</code></a></td></tr>
   <tr><td width="15%"><a 
href="sitemgr.SiteMgr-class.html#__init__"><code>__init__</code></a></td>
     <td>Method in class <a 
href="sitemgr.SiteMgr-class.html"><code>sitemgr.SiteMgr</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#__repr__"><code>__repr__</code></a></td>
+    <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
   <tr><td width="15%"><a 
href="exceptions.Exception-class.html#__str__"><code>__str__</code></a></td>
     <td>Method in class <a 
href="exceptions.Exception-class.html"><code>exceptions.Exception</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp.FCPException-class.html#__str__"><code>__str__</code></a></td>
     <td>Method in class <a 
href="fcp.FCPException-class.html"><code>fcp.FCPException</code></a></td></tr>
+  <tr><td width="15%"><a 
href="../private/fcp.JobTicket-class.html#_appendMsg"><code>_appendMsg</code></a></td>
+    <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
   <tr><td width="15%"><a 
href="../private/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html#_dispatch"><code>_dispatch</code></a></td>
     <td>Method in class <a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html"><code>SimpleXMLRPCServer.SimpleXMLRPCDispatcher</code></a></td></tr>
   <tr><td width="15%"><a 
href="../private/fcp.FCPNodeConnection-class.html#_getUniqueId"><code>_getUniqueId</code></a></td>
@@ -79,6 +83,8 @@
     <td>Method in class <a 
href="fcp.FCPNodeConnection-class.html"><code>fcp.FCPNodeConnection</code></a></td></tr>
   <tr><td width="15%"><a 
href="../private/fcp.FCPNodeConnection-class.html#_log"><code>_log</code></a></td>
     <td>Method in class <a 
href="fcp.FCPNodeConnection-class.html"><code>fcp.FCPNodeConnection</code></a></td></tr>
+  <tr><td width="15%"><a 
href="../private/sitemgr.SiteMgr-class.html#_log"><code>_log</code></a></td>
+    <td>Method in class <a 
href="sitemgr.SiteMgr-class.html"><code>sitemgr.SiteMgr</code></a></td></tr>
   <tr><td width="15%"><a 
href="../private/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html#_marshaled_dispatch"><code>_marshaled_dispatch</code></a></td>
     <td>Method in class <a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html"><code>SimpleXMLRPCServer.SimpleXMLRPCDispatcher</code></a></td></tr>
   <tr><td width="15%"><a 
href="../private/fcp.FCPNodeConnection-class.html#_mgrThread"><code>_mgrThread</code></a></td>
@@ -113,6 +119,8 @@
     <td>Class in module <code>SocketServer</code></td></tr>
   <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#callback"><code>callback</code></a></td>
     <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#cancel"><code>cancel</code></a></td>
+    <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
   <tr><td width="15%"><a 
href="../private/SocketServer.BaseServer-class.html#close_request"><code>close_request</code></a></td>
     <td>Method in class <a 
href="../private/SocketServer.BaseServer-class.html"><code>SocketServer.BaseServer</code></a></td></tr>
   <tr><td width="15%"><a 
href="SocketServer.TCPServer-class.html#close_request"><code>close_request</code></a></td>
@@ -181,6 +189,8 @@
     <td>Method in class <a 
href="fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html"><code>fcpxmlrpc.FreenetXMLRPCRequestHandler</code></a></td></tr>
   <tr><td width="15%"><a 
href="SocketServer.TCPServer-class.html#get_request"><code>get_request</code></a></td>
     <td>Method in class <a 
href="SocketServer.TCPServer-class.html"><code>SocketServer.TCPServer</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#getResult"><code>getResult</code></a></td>
+    <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp-module.html#guessMimetype"><code>guessMimetype</code></a></td>
     <td>Function in module <a 
href="fcp-module.html"><code>fcp</code></a></td></tr>
   <tr><td width="15%"><a 
href="../private/SocketServer.BaseServer-class.html#handle_error"><code>handle_error</code></a></td>
@@ -219,6 +229,8 @@
     <td>Method in class <a 
href="fcp.FCPNodeConnection-class.html"><code>fcp.FCPNodeConnection</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp-module.html#readdir"><code>readdir</code></a></td>
     <td>Function in module <a 
href="fcp-module.html"><code>fcp</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.FCPNodeConnection-class.html#refreshPersistentRequests"><code>refreshPersistentRequests</code></a></td>
+    <td>Method in class <a 
href="fcp.FCPNodeConnection-class.html"><code>fcp.FCPNodeConnection</code></a></td></tr>
   <tr><td width="15%"><a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html#register_function"><code>register_function</code></a></td>
     <td>Method in class <a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html"><code>SimpleXMLRPCServer.SimpleXMLRPCDispatcher</code></a></td></tr>
   <tr><td width="15%"><a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html#register_instance"><code>register_instance</code></a></td>
@@ -322,7 +334,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/sitemgr-module.html
===================================================================
--- trunk/apps/pyFreenet/html/private/sitemgr-module.html       2006-05-13 
21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/sitemgr-module.html       2006-05-14 
05:23:40 UTC (rev 8697)
@@ -185,7 +185,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/sitemgr.SiteMgr-class.html
===================================================================
--- trunk/apps/pyFreenet/html/private/sitemgr.SiteMgr-class.html        
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/private/sitemgr.SiteMgr-class.html        
2006-05-14 05:23:40 UTC (rev 8697)
@@ -84,6 +84,12 @@
   <td><code><span class="summary-sig"><a 
href="sitemgr.SiteMgr-class.html#update" 
class="summary-sig-name"><code>update</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
 <br />
 Insert/update all registered freesites</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><span class="summary-sig"><a 
href="../private/sitemgr.SiteMgr-class.html#_log" 
class="summary-sig-name"><code>_log</code></a>(<span 
class=summary-sig-arg>self</span>,
+          <span class=summary-sig-arg>level</span>,
+          <span class=summary-sig-arg>msg</span>)</span></code>
+<br />
+Logs a message.</td></tr>
 </table><br />


@@ -108,6 +114,13 @@
       defaults to ~/.freesitesrc on *nix or ~/freesites.ini
     </li>
   </ul>
+  Keywords:
+  <ul>
+    <li>
+      logfile - a pathname or open file object to which to write log 
+      messages, defaults to sys.stdout
+    </li>
+  </ul>
   <dl><dt></dt><dd>
   </dd></dl>
 </td></tr></table>
@@ -148,6 +161,17 @@
   <dl><dt></dt><dd>
   </dd></dl>
 </td></tr></table>
+
+<a name="_log"></a>
+<table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
+  <h3><span class="sig"><span class="sig-name">_log</span>(<span 
class=sig-arg>self</span>,
+          <span class=sig-arg>level</span>,
+          <span class=sig-arg>msg</span>)</span>
+  </h3>
+  Logs a message. If level &gt; verbosity, don't output it
+  <dl><dt></dt><dd>
+  </dd></dl>
+</td></tr></table>
 <br />


@@ -169,7 +193,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/private/trees.html
===================================================================
--- trunk/apps/pyFreenet/html/private/trees.html        2006-05-13 21:59:31 UTC 
(rev 8696)
+++ trunk/apps/pyFreenet/html/private/trees.html        2006-05-14 05:23:40 UTC 
(rev 8697)
@@ -120,7 +120,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: 
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
===================================================================
--- 
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
       2006-05-13 21:59:31 UTC (rev 8696)
+++ 
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html
       2006-05-14 05:23:40 UTC (rev 8697)
@@ -258,7 +258,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: 
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
===================================================================
--- 
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
   2006-05-13 21:59:31 UTC (rev 8696)
+++ 
trunk/apps/pyFreenet/html/public/SimpleXMLRPCServer.SimpleXMLRPCServer-class.html
   2006-05-14 05:23:40 UTC (rev 8697)
@@ -186,7 +186,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/SocketServer.TCPServer-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/SocketServer.TCPServer-class.html  
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/SocketServer.TCPServer-class.html  
2006-05-14 05:23:40 UTC (rev 8697)
@@ -348,7 +348,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: 
trunk/apps/pyFreenet/html/public/SocketServer.ThreadingMixIn-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/SocketServer.ThreadingMixIn-class.html     
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/SocketServer.ThreadingMixIn-class.html     
2006-05-14 05:23:40 UTC (rev 8697)
@@ -157,7 +157,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/exceptions.Exception-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/exceptions.Exception-class.html    
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/exceptions.Exception-class.html    
2006-05-14 05:23:40 UTC (rev 8697)
@@ -86,7 +86,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp-module.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp-module.html    2006-05-13 21:59:31 UTC 
(rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp-module.html    2006-05-14 05:23:40 UTC 
(rev 8697)
@@ -458,7 +458,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp.ConnectionRefused-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp.ConnectionRefused-class.html   
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp.ConnectionRefused-class.html   
2006-05-14 05:23:40 UTC (rev 8697)
@@ -89,7 +89,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp.FCPException-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp.FCPException-class.html        
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp.FCPException-class.html        
2006-05-14 05:23:40 UTC (rev 8697)
@@ -93,7 +93,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp.FCPGetFailed-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp.FCPGetFailed-class.html        
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp.FCPGetFailed-class.html        
2006-05-14 05:23:40 UTC (rev 8697)
@@ -91,7 +91,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp.FCPNodeConnection-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp.FCPNodeConnection-class.html   
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp.FCPNodeConnection-class.html   
2006-05-14 05:23:40 UTC (rev 8697)
@@ -132,6 +132,12 @@
 <br />
 Inserts a freesite</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><span class="summary-sig"><a 
href="fcp.FCPNodeConnection-class.html#refreshPersistentRequests" 
class="summary-sig-name"><code>refreshPersistentRequests</code></a>(<span 
class=summary-sig-arg>self</span>,
+          <span class="summary-sig-kwarg">**kw</span>)</span></code>
+<br />
+Sends a ListPersistentRequests to node, to ensure that our records of 
+persistent requests are up to date.</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.FCPNodeConnection-class.html#shutdown" 
class="summary-sig-name"><code>shutdown</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
 <br />
 Terminates the manager thread</td></tr>
@@ -151,11 +157,11 @@
     <br /><i>(Constructor)</i>
   </h3>
   <p>Create a connection object</p>
-  Arguments:
+  Keyword Arguments:
   <ul>
     <li>
-      clientName - name of client to use with reqs, defaults to 
-      random
+      name - name of client to use with reqs, defaults to random. This 
+      is crucial if you plan on making persistent requests
     </li>
     <li>
       host - hostname, defaults to defaultFCPHost
@@ -172,6 +178,30 @@
       0 (silence)
     </li>
   </ul>
+  Attributes of interest:
+  <ul>
+    <li>
+      jobs - a dict of currently running jobs (persistent and 
+      nonpersistent). keys are job ids and values are JobTicket 
+      objects
+    </li>
+    <li>
+      persistentJobs - a dict of persistent jobs from this session and 
+      previous sessions keys are job ids and values are JobTicket 
+      objects
+    </li>
+  </ul>
+  Notes:
+  <ul>
+    <li>
+      <p>when the connection is created, a 'hello' handshake takes place. 
+      After that handshake, the node sends back a list of outstanding 
+      persistent requests left over from the last connection (based on 
+      the value of the 'name' keyword passed into this constructor).</p>
+      This object then wraps all this info into JobTicket instances 
+      and stores them in the self.persistentJobs dict
+    </li>
+  </ul>
   <dl><dt></dt><dd>
   </dd></dl>
 </td></tr></table>
@@ -240,6 +270,11 @@
       default False (wait for completion)
     </li>
     <li>
+      persistence - default 'connection' - the kind of persistence for 
+      this request. If 'reboot' or 'forever', this job will be able to be 
+      recalled in subsequent FCP sessions
+    </li>
+    <li>
       dsnly - whether to only check local datastore
     </li>
     <li>
@@ -336,15 +371,20 @@
   Keywords valid for all modes:
   <ul>
     <li>
+      async - whether to do the job asynchronously, returning a job 
+      ticket object (default False)
+    </li>
+    <li>
+      persistence - default 'connection' - the kind of persistence for 
+      this request. If 'reboot' or 'forever', this job will be able to be 
+      recalled in subsequent FCP sessions
+    </li>
+    <li>
       maxretries - maximum number of retries, default 3
     </li>
     <li>
       priority - default 1
     </li>
-    <li>
-      async - whether to do the job asynchronously, returning a job 
-      ticket object (default False)
-    </li>
   </ul>
   Notes:
   <ul>
@@ -392,8 +432,14 @@
       priority - default 1
     </li>
     <li>
-      async - default False - if True, return a job ticket
+      async - default False - if True, return immediately with a job 
+      ticket
     </li>
+    <li>
+      persistence - default 'connection' - the kind of persistence for 
+      this request. If 'reboot' or 'forever', this job will be able to be 
+      recalled in subsequent FCP sessions
+    </li>
   </ul>
   Returns:
   <ul>
@@ -405,6 +451,20 @@
   </dd></dl>
 </td></tr></table>

+<a name="refreshPersistentRequests"></a>
+<table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
+  <h3><span class="sig"><span 
class="sig-name">refreshPersistentRequests</span>(<span 
class=sig-arg>self</span>,
+          <span class="sig-kwarg">**kw</span>)</span>
+  </h3>
+  <p>Sends a ListPersistentRequests to node, to ensure that our records 
+  of persistent requests are up to date.</p>
+  Since, upon connection, the node sends us a list of all outstanding 
+  persistent requests anyway, I can't really see much use for this 
+  method. I've only added the method for FCP spec compliance
+  <dl><dt></dt><dd>
+  </dd></dl>
+</td></tr></table>
+
 <a name="shutdown"></a>
 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
   <h3><span class="sig"><span class="sig-name">shutdown</span>(<span 
class=sig-arg>self</span>)</span>
@@ -436,7 +496,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp.FCPProtocolError-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp.FCPProtocolError-class.html    
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp.FCPProtocolError-class.html    
2006-05-14 05:23:40 UTC (rev 8697)
@@ -91,7 +91,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp.FCPPutFailed-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp.FCPPutFailed-class.html        
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp.FCPPutFailed-class.html        
2006-05-14 05:23:40 UTC (rev 8697)
@@ -91,7 +91,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcp.JobTicket-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcp.JobTicket-class.html   2006-05-13 
21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcp.JobTicket-class.html   2006-05-14 
05:23:40 UTC (rev 8697)
@@ -67,12 +67,17 @@
   <th colspan="2">Method Summary</th></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#__init__" 
class="summary-sig-name"><code>__init__</code></a>(<span 
class=summary-sig-arg>self</span>,
+          <span class=summary-sig-arg>node</span>,
           <span class=summary-sig-arg>id</span>,
           <span class=summary-sig-arg>cmd</span>,
+          <span class=summary-sig-arg>persistent</span>,
           <span class=summary-sig-arg>kw</span>)</span></code>
 <br />
 You should never instantiate a JobTicket object yourself</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><a name="__repr__"></a><span class="summary-sig"><span 
class="summary-sig-name">__repr__</span>(<span 
class=summary-sig-arg>self</span>)</span></code>
+</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#callback" 
class="summary-sig-name"><code>callback</code></a>(<span 
class=summary-sig-arg>self</span>,
           <span class=summary-sig-arg>status</span>,
           <span class=summary-sig-arg>value</span>)</span></code>
@@ -80,6 +85,14 @@
 This will be replaced in job ticket instances wherever user provides 
 callback arguments</td></tr>
 <tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#cancel" 
class="summary-sig-name"><code>cancel</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
+<br />
+Cancels the job, if it is persistent</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
+  <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#getResult" 
class="summary-sig-name"><code>getResult</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
+<br />
+Returns result of job, or None if job still not complete</td></tr>
+<tr><td align="right" valign="top" width="15%"><font 
size="-1">&nbsp;</font></td>
   <td><code><span class="summary-sig"><a 
href="fcp.JobTicket-class.html#isComplete" 
class="summary-sig-name"><code>isComplete</code></a>(<span 
class=summary-sig-arg>self</span>)</span></code>
 <br />
 Returns True if the job has been completed</td></tr>
@@ -100,8 +113,10 @@
 <a name="__init__"></a>
 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
   <h3><span class="sig"><span class="sig-name">__init__</span>(<span 
class=sig-arg>self</span>,
+          <span class=sig-arg>node</span>,
           <span class=sig-arg>id</span>,
           <span class=sig-arg>cmd</span>,
+          <span class=sig-arg>persistent</span>,
           <span class=sig-arg>kw</span>)</span>
     <br /><i>(Constructor)</i>
   </h3>
@@ -122,6 +137,26 @@
   </dd></dl>
 </td></tr></table>

+<a name="cancel"></a>
+<table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
+  <h3><span class="sig"><span class="sig-name">cancel</span>(<span 
class=sig-arg>self</span>)</span>
+  </h3>
+  <p>Cancels the job, if it is persistent</p>
+  Does nothing if the job was not persistent
+  <dl><dt></dt><dd>
+  </dd></dl>
+</td></tr></table>
+
+<a name="getResult"></a>
+<table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
+  <h3><span class="sig"><span class="sig-name">getResult</span>(<span 
class=sig-arg>self</span>)</span>
+  </h3>
+  <p>Returns result of job, or None if job still not complete</p>
+  If result is an exception object, then raises it
+  <dl><dt></dt><dd>
+  </dd></dl>
+</td></tr></table>
+
 <a name="isComplete"></a>
 <table width="100%" class="func-details" bgcolor="#e0e0e0"><tr><td>
   <h3><span class="sig"><span class="sig-name">isComplete</span>(<span 
class=sig-arg>self</span>)</span>
@@ -161,7 +196,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcpxmlrpc-module.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcpxmlrpc-module.html      2006-05-13 
21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcpxmlrpc-module.html      2006-05-14 
05:23:40 UTC (rev 8697)
@@ -185,7 +185,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/fcpxmlrpc.FCPXMLRPCServer-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/fcpxmlrpc.FCPXMLRPCServer-class.html       
2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/fcpxmlrpc.FCPXMLRPCServer-class.html       
2006-05-14 05:23:40 UTC (rev 8697)
@@ -254,7 +254,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: 
trunk/apps/pyFreenet/html/public/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
===================================================================
--- 
trunk/apps/pyFreenet/html/public/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
   2006-05-13 21:59:31 UTC (rev 8696)
+++ 
trunk/apps/pyFreenet/html/public/fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html
   2006-05-14 05:23:40 UTC (rev 8697)
@@ -142,7 +142,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/help.html
===================================================================
--- trunk/apps/pyFreenet/html/public/help.html  2006-05-13 21:59:31 UTC (rev 
8696)
+++ trunk/apps/pyFreenet/html/public/help.html  2006-05-14 05:23:40 UTC (rev 
8697)
@@ -225,7 +225,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/indices.html
===================================================================
--- trunk/apps/pyFreenet/html/public/indices.html       2006-05-13 21:59:31 UTC 
(rev 8696)
+++ trunk/apps/pyFreenet/html/public/indices.html       2006-05-14 05:23:40 UTC 
(rev 8697)
@@ -65,6 +65,8 @@
     <td>Method in class <a 
href="fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html"><code>fcpxmlrpc.FreenetXMLRPCRequestHandler</code></a></td></tr>
   <tr><td width="15%"><a 
href="sitemgr.SiteMgr-class.html#__init__"><code>__init__</code></a></td>
     <td>Method in class <a 
href="sitemgr.SiteMgr-class.html"><code>sitemgr.SiteMgr</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#__repr__"><code>__repr__</code></a></td>
+    <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
   <tr><td width="15%"><a 
href="exceptions.Exception-class.html#__str__"><code>__str__</code></a></td>
     <td>Method in class <a 
href="exceptions.Exception-class.html"><code>exceptions.Exception</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp.FCPException-class.html#__str__"><code>__str__</code></a></td>
@@ -83,6 +85,8 @@
     <td>Variable in class <a 
href="SocketServer.TCPServer-class.html"><code>SocketServer.TCPServer</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#callback"><code>callback</code></a></td>
     <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#cancel"><code>cancel</code></a></td>
+    <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
   <tr><td width="15%"><a 
href="SocketServer.TCPServer-class.html#close_request"><code>close_request</code></a></td>
     <td>Method in class <a 
href="SocketServer.TCPServer-class.html"><code>SocketServer.TCPServer</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp.ConnectionRefused-class.html"><code>ConnectionRefused</code></a></td>
@@ -147,6 +151,8 @@
     <td>Method in class <a 
href="fcpxmlrpc.FreenetXMLRPCRequestHandler-class.html"><code>fcpxmlrpc.FreenetXMLRPCRequestHandler</code></a></td></tr>
   <tr><td width="15%"><a 
href="SocketServer.TCPServer-class.html#get_request"><code>get_request</code></a></td>
     <td>Method in class <a 
href="SocketServer.TCPServer-class.html"><code>SocketServer.TCPServer</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.JobTicket-class.html#getResult"><code>getResult</code></a></td>
+    <td>Method in class <a 
href="fcp.JobTicket-class.html"><code>fcp.JobTicket</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp-module.html#guessMimetype"><code>guessMimetype</code></a></td>
     <td>Function in module <a 
href="fcp-module.html"><code>fcp</code></a></td></tr>
   <tr><td width="15%"><a 
href="sitemgr-module.html#help"><code>help</code></a></td>
@@ -179,6 +185,8 @@
     <td>Method in class <a 
href="fcp.FCPNodeConnection-class.html"><code>fcp.FCPNodeConnection</code></a></td></tr>
   <tr><td width="15%"><a 
href="fcp-module.html#readdir"><code>readdir</code></a></td>
     <td>Function in module <a 
href="fcp-module.html"><code>fcp</code></a></td></tr>
+  <tr><td width="15%"><a 
href="fcp.FCPNodeConnection-class.html#refreshPersistentRequests"><code>refreshPersistentRequests</code></a></td>
+    <td>Method in class <a 
href="fcp.FCPNodeConnection-class.html"><code>fcp.FCPNodeConnection</code></a></td></tr>
   <tr><td width="15%"><a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html#register_function"><code>register_function</code></a></td>
     <td>Method in class <a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html"><code>SimpleXMLRPCServer.SimpleXMLRPCDispatcher</code></a></td></tr>
   <tr><td width="15%"><a 
href="SimpleXMLRPCServer.SimpleXMLRPCDispatcher-class.html#register_instance"><code>register_instance</code></a></td>
@@ -274,7 +282,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/sitemgr-module.html
===================================================================
--- trunk/apps/pyFreenet/html/public/sitemgr-module.html        2006-05-13 
21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/sitemgr-module.html        2006-05-14 
05:23:40 UTC (rev 8697)
@@ -185,7 +185,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/sitemgr.SiteMgr-class.html
===================================================================
--- trunk/apps/pyFreenet/html/public/sitemgr.SiteMgr-class.html 2006-05-13 
21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/html/public/sitemgr.SiteMgr-class.html 2006-05-14 
05:23:40 UTC (rev 8697)
@@ -108,6 +108,13 @@
       defaults to ~/.freesitesrc on *nix or ~/freesites.ini
     </li>
   </ul>
+  Keywords:
+  <ul>
+    <li>
+      logfile - a pathname or open file object to which to write log 
+      messages, defaults to sys.stdout
+    </li>
+  </ul>
   <dl><dt></dt><dd>
   </dd></dl>
 </td></tr></table>
@@ -169,7 +176,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/html/public/trees.html
===================================================================
--- trunk/apps/pyFreenet/html/public/trees.html 2006-05-13 21:59:31 UTC (rev 
8696)
+++ trunk/apps/pyFreenet/html/public/trees.html 2006-05-14 05:23:40 UTC (rev 
8697)
@@ -108,7 +108,7 @@

 <table border="0" cellpadding="0" cellspacing="0" width="100%">
   <tr>
-    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sat May 13 
18:31:10 2006</font></td>
+    <td align="left"><font size="-2">Generated by Epydoc 2.1 on Sun May 14 
17:22:43 2006</font></td>
     <td align="right"><a href="http://epydoc.sourceforge.net";
                       ><font size="-2">http://epydoc.sf.net</font></a></td>
   </tr>

Modified: trunk/apps/pyFreenet/sitemgr.py
===================================================================
--- trunk/apps/pyFreenet/sitemgr.py     2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/sitemgr.py     2006-05-14 05:23:40 UTC (rev 8697)
@@ -4,6 +4,9 @@
 """
 import fcp, sys, os, sha, traceback

+# get log level constants
+from fcp import SILENT, FATAL, CRITICAL, ERROR, INFO, DETAIL, DEBUG
+
 from ConfigParser import SafeConfigParser

 fcpHost = "thoth"
@@ -23,9 +26,23 @@
         Arguments:
             - configFile - ini-format file containing site specifications,
               defaults to ~/.freesitesrc on *nix or ~/freesites.ini
+    
+        Keywords:
+            - logfile - a pathname or open file object to which to write
+              log messages, defaults to sys.stdout
         """
+        # set up the logger
+        logfile = kw.pop('logfile', sys.stderr)
+        if not hasattr(logfile, 'write'):
+            # might be a pathname
+            if not isinstance(logfile, str):
+                raise Exception("Bad logfile, must be pathname or file object")
+            logfile = file(logfile, "a")
+        self.logfile = logfile
+        self.verbosity = kw.get('verbosity', 0)
+    
         # get a node handle
-        self.createNode(**kw)
+        self.createNode(logfile=logfile, **kw)

         # determine pathname for sites ini file
         if configFile == None:
@@ -41,8 +58,8 @@

         if not os.path.isfile(configFile):
             self.createConfig()
-            print "New config file created at %s"
-            print "Please edit that file and add your freesites"
+            self._log(CRITICAL, "New config file created at %s" % configFile)
+            self._log(CRITICAL, "Please edit that file and add your freesites")

         self.loadConfig()

@@ -148,6 +165,8 @@
         """
         noSites = True

+        log = self._log
+    
         conf = self.config
         for sitename in conf.sections():
             uri = conf.get(sitename, "uri")
@@ -162,8 +181,8 @@
                 h.update(f['hash'])
             hashNew = h.hexdigest()
             if hashNew != hash:
-                print "Updating site %s" % sitename
-                print "privatekey=%s" % privatekey
+                log(INFO, "Updating site %s" % sitename)
+                log(INFO, "privatekey=%s" % privatekey)
                 noSites = False
                 try:
                     res = self.node.put(privatekey,
@@ -171,20 +190,32 @@
                                         name=sitename,
                                         version=version,
                                         usk=True)
-                    print "site %s updated successfully" % sitename
+                    log(INFO, "site %s updated successfully" % sitename)
                 except:
                     traceback.print_exc()
-                    print "site %s failed to update" % sitename
+                    log(ERROR, "site %s failed to update" % sitename)
                 conf.set(sitename, "hash", hashNew)

         self.saveConfig()

         if noSites:
-            print "No sites needed updating"
+            log(INFO, "No sites needed updating")

     def shutdown(self):
         self.node.shutdown()

+    def _log(self, level, msg):
+        """
+        Logs a message. If level > verbosity, don't output it
+        """
+        if level > self.verbosity:
+            return
+    
+        if not msg.endswith("\n"): msg += "\n"
+    
+        self.logfile.write(msg)
+        self.logfile.flush()
+    

 def help():
     print "%s: A console-based, cron-able freesite inserter" % sys.argv[0]

Modified: trunk/apps/pyFreenet/updatesites.py
===================================================================
--- trunk/apps/pyFreenet/updatesites.py 2006-05-13 21:59:31 UTC (rev 8696)
+++ trunk/apps/pyFreenet/updatesites.py 2006-05-14 05:23:40 UTC (rev 8697)
@@ -1,5 +1,7 @@
 #!/usr/bin/env python
-
+"""
+A utility to update freesites from within a cron environment
+"""
 import sys, os, time, commands
 import sitemgr

@@ -14,8 +16,11 @@
 # derive path of freenet pid file, the (non)existence
 # of which is the easiest test of whether the freenet
 # node is running
-pidFile = os.path.join(freenetDir, "Freenet.pid")
+freenetPidFile = os.path.join(freenetDir, "Freenet.pid")

+logFile = os.path.join(freenetDir, "updatesites.log")
+pidFile = os.path.join(freenetDir, "updatesites.pid")
+
 # small wrapper which, if freenet isn't already running,
 # starts it prior to inserting then stops it after
 # inserting
@@ -24,40 +29,53 @@
     if verbose == None:
         verbose = ('-v' in sys.argv)

-    print "--------------------------------------------"
-    print "Start of site updating run"
+    if os.path.isfile(pidFile):
+        print "updatesites.py already running: pid=%s" % file(pidFile).read()
+        sys.exit(1)
+    f = file(pidFile, "w")
+    f.write(str(os.getpid()))
+    f.close()
+
+    try:
+        print "--------------------------------------------"
+        print "Start of site updating run"
+        
+        # start freenet and let it warm up, if it's not already running
+        if not os.path.isfile(freenetPidFile):
+            startingFreenet = True
+            os.chdir(freenetDir)
+            print "Starting freenet..."
+            print os.system("%s/start.sh &" % freenetDir)
+            print "Letting node settle for %s seconds..." % startupTime
+            time.sleep(startupTime)
+        else:
+            print "Freenet node is already running!"
+            startingFreenet = False

-    # start freenet and let it warm up, if it's not already running
-    if not os.path.isfile(pidFile):
-        startingFreenet = True
-        os.chdir(freenetDir)
-        print "Starting freenet..."
-        print os.system("%s/start.sh &" % freenetDir)
-        print "Letting node settle for %s seconds..." % startupTime
-        time.sleep(startupTime)
-    else:
-        print "Freenet node is already running!"
-        startingFreenet = False
+        # add verbosity argument if needed    
+        if verbose:
+            kw = {"verbosity" : sitemgr.fcp.DETAIL}
+        else:
+            kw = {"verbosity" : sitemgr.fcp.INFO}
+    
+        # get a site manager object, and perform the actual insertions
+        s = sitemgr.SiteMgr(**kw)
+        s.update()
+        del s
+        
+        # kill freenet if it was dynamically started
+        if startingFreenet:
+            print "Waiting %s for inserts to finish..." % startupTime
+            time.sleep(startupTime)
+            print "Stopping node..."
+            os.system("./run.sh stop")
+            print "Node stopped"
+    except:
+        pass

-    # add verbosity argument if needed    
-    if verbose:
-        kw = {"verbosity" : sitemgr.fcp.DETAIL}
-    else:
-        kw = {}
+    # can now drop our pidfile
+    os.unlink(pidFile)

-    # get a site manager object, and perform the actual insertions
-    s = sitemgr.SiteMgr(**kw)
-    s.update()
-    del s
-    
-    # kill freenet if it was dynamically started
-    if startingFreenet:
-        print "Waiting %s for inserts to finish..." % startupTime
-        time.sleep(startupTime)
-        print "Stopping node..."
-        os.system("./run.sh stop")
-        print "Node stopped"
-
 if __name__ == '__main__':
     main()



Reply via email to