From 3998972e544c25df78589b02193c401e24fc3cde Mon Sep 17 00:00:00 2001 From: Cameron Dale Date: Tue, 4 Mar 2008 16:02:43 -0800 Subject: [PATCH] Document the DHT's actions module. --- apt_dht_Khashmir/actions.py | 104 +++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 13 deletions(-) diff --git a/apt_dht_Khashmir/actions.py b/apt_dht_Khashmir/actions.py index e244c49..cc98610 100644 --- a/apt_dht_Khashmir/actions.py +++ b/apt_dht_Khashmir/actions.py @@ -1,6 +1,8 @@ ## Copyright 2002-2004 Andrew Loewenstern, All Rights Reserved # see LICENSE.txt for license information +"""Details of how to perform actions on remote peers.""" + from twisted.internet import reactor from twisted.python import log @@ -8,9 +10,64 @@ from khash import intify from util import uncompact class ActionBase: - """ base class for some long running asynchronous proccesses like finding nodes or values """ + """Base class for some long running asynchronous proccesses like finding nodes or values. + + @type caller: L{khashmir.Khashmir} + @ivar caller: the DHT instance that is performing the action + @type target: C{string} + @ivar target: the target of the action, usually a DHT key + @type config: C{dictionary} + @ivar config: the configuration variables for the DHT + @type action: C{string} + @ivar action: the name of the action to call on remote nodes + @type num: C{long} + @ivar num: the target key in integer form + @type queried: C{dictionary} + @ivar queried: the nodes that have been queried for this action, + keys are node IDs, values are the node itself + @type answered: C{dictionary} + @ivar answered: the nodes that have answered the queries + @type found: C{dictionary} + @ivar found: nodes that have been found so far by the action + @type sorted_nodes: C{list} of L{node.Node} + @ivar sorted_nodes: a sorted list of nodes by there proximity to the key + @type results: C{dictionary} + @ivar results: keys are the results found so far by the action + @type desired_results: C{int} + @ivar desired_results: the minimum number of results that are needed + before the action should stop + @type callback: C{method} + @ivar callback: the method to call with the results + @type outstanding: C{int} + @ivar outstanding: the number of requests currently outstanding + @type outstanding_results: C{int} + @ivar outstanding_results: the number of results that are expected from + the requests that are currently outstanding + @type finished: C{boolean} + @ivar finished: whether the action is done + @type sort: C{method} + @ivar sort: used to sort nodes by their proximity to the target + """ + def __init__(self, caller, target, callback, config, action, num_results = None): - """Initialize the action.""" + """Initialize the action. + + @type caller: L{khashmir.Khashmir} + @param caller: the DHT instance that is performing the action + @type target: C{string} + @param target: the target of the action, usually a DHT key + @type callback: C{method} + @param callback: the method to call with the results + @type config: C{dictionary} + @param config: the configuration variables for the DHT + @type action: C{string} + @param action: the name of the action to call on remote nodes + @type num_results: C{int} + @param num_results: the minimum number of results that are needed before + the action should stop (optional, defaults to getting all the results) + + """ + self.caller = caller self.target = target self.config = config @@ -36,7 +93,8 @@ class ActionBase: return -1 return 0 self.sort = sort - + + #{ Main operation def goWithNodes(self, nodes): """Start the action's process with a list of nodes to contact.""" for node in nodes: @@ -61,7 +119,9 @@ class ActionBase: len(self.results) + self.outstanding_results >= abs(self.desired_results)): return + # Loop for each node that should be processed for node in self.getNodesToProcess(): + # Don't send requests twice or to ourself if node.id not in self.queried and node.id != self.caller.node.id: self.queried[node.id] = 1 @@ -122,7 +182,11 @@ class ActionBase: self.schedule() def handleGotNodes(self, nodes): - """Process any received node contact info in the response.""" + """Process any received node contact info in the response. + + Not called by default, but suitable for being called by + L{processResponse} in a recursive node search. + """ for compact_node in nodes: node_contact = uncompact(compact_node) node = self.caller.Node(node_contact) @@ -138,7 +202,7 @@ class ActionBase: self.sorted_nodes = self.found.values() self.sorted_nodes.sort(self.sort) - # The methods below are meant to be subclassed by actions + #{ Subclass for specific actions def getNodesToProcess(self): """Generate a list of nodes to process next. @@ -162,7 +226,7 @@ class ActionBase: self.handleGotNodes(dict['nodes']) def generateResult(self, nodes): - """Create the result to return to the callback function.""" + """Create the final result to return to the L{callback} function.""" return [] @@ -185,7 +249,7 @@ class FindNode(ActionBase): class FindValue(ActionBase): - """Find the closest nodes to the key and check their values.""" + """Find the closest nodes to the key and check for values.""" def __init__(self, caller, target, callback, config, action="findValue"): ActionBase.__init__(self, caller, target, callback, config, action) @@ -203,18 +267,25 @@ class FindValue(ActionBase): class GetValue(ActionBase): + """Retrieve values from a list of nodes.""" + def __init__(self, caller, target, local_results, num_results, callback, config, action="getValue"): + """Initialize the action with the locally available results. + + @type local_results: C{list} of C{string} + @param local_results: the values that were available in this node + """ ActionBase.__init__(self, caller, target, callback, config, action, num_results) if local_results: for result in local_results: self.results[result] = 1 def getNodesToProcess(self): - """Nodes are never added, always return the same thing.""" + """Nodes are never added, always return the same sorted node list.""" return self.sorted_nodes def generateArgs(self, node): - """Args include the number of values to request.""" + """Arguments include the number of values to request.""" if node.num_values > 0: # Request all desired results from each node, just to be sure. num_values = abs(self.desired_results) - len(self.results) @@ -226,7 +297,7 @@ class GetValue(ActionBase): raise ValueError, "Don't try and get values from this node because it doesn't have any" def processResponse(self, dict): - """Save the returned values, calling the callback each time there are new ones.""" + """Save the returned values, calling the L{callback} each time there are new ones.""" if dict.has_key('values'): def x(y, z=self.results): if not z.has_key(y): @@ -240,21 +311,28 @@ class GetValue(ActionBase): reactor.callLater(0, self.callback, self.target, v) def generateResult(self): - """Results have all been returned, now send the empty list to end it.""" + """Results have all been returned, now send the empty list to end the action.""" return (self.target, []) class StoreValue(ActionBase): + """Store a value in a list of nodes.""" + def __init__(self, caller, target, value, num_results, callback, config, action="storeValue"): + """Initialize the action with the value to store. + + @type value: C{string} + @param value: the value to store in the nodes + """ ActionBase.__init__(self, caller, target, callback, config, action, num_results) self.value = value def getNodesToProcess(self): - """Nodes are never added, always return the same thing.""" + """Nodes are never added, always return the same sorted list.""" return self.sorted_nodes def generateArgs(self, node): - """Args include the value to request and the node's token.""" + """Args include the value to store and the node's token.""" if node.token: return (self.target, self.value, node.token), 1 else: -- 2.39.5