diff --git a/docs/index.rst b/docs/index.rst index b690320..6148bba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,6 +20,7 @@ This library aims to be as close to a reference implementation of the `Kademlia :titlesonly: intro + querying source/modules diff --git a/docs/querying.rst b/docs/querying.rst new file mode 100644 index 0000000..9b6c351 --- /dev/null +++ b/docs/querying.rst @@ -0,0 +1,12 @@ +Querying the DHT +================== + +If you just want to query the network, you can use the example query script. For instance:: + + $ python examples/query.py 1.2.3.4 8468 SpecialKey + +The query script is simple: + +.. literalinclude:: ../examples/query.py + +Check out the examples folder for other examples. diff --git a/examples/query.py b/examples/query.py new file mode 100644 index 0000000..18e94e5 --- /dev/null +++ b/examples/query.py @@ -0,0 +1,33 @@ +from twisted.internet import reactor +from twisted.python import log +from kademlia.network import Server +import sys + +log.startLogging(sys.stdout) + +if len(sys.argv) != 4: + print "Usage: python query.py " + sys.exit(1) + +ip = sys.argv[1] +port = int(sys.argv[2]) +key = sys.argv[3] + +print "Getting %s (with bootstrap %s:%i)" % (key, ip, port) + +def done(result): + print "Key result:" + print result + reactor.stop() + +def bootstrapDone(found, server, key): + if len(found) == 0: + print "Could not connect to the bootstrap server." + reactor.stop() + server.get(key).addCallback(done) + +server = Server() +server.listen(port) +server.bootstrap([(ip, port)]).addCallback(bootstrapDone, server, key) + +reactor.run() diff --git a/kademlia/routing.py b/kademlia/routing.py index d8860f9..a79a988 100644 --- a/kademlia/routing.py +++ b/kademlia/routing.py @@ -152,7 +152,7 @@ class RoutingTable(object): if bucket.addNode(node): return - # Per section 4.2 of paper, split if the bucket has the node in it's range + # Per section 4.2 of paper, split if the bucket has the node in its range # or if the depth is not congruent to 0 mod 5 if bucket.hasInRange(self.node) or bucket.depth() % 5 != 0: self.splitBucket(index) diff --git a/kademlia/tests/test_utils.py b/kademlia/tests/test_utils.py new file mode 100644 index 0000000..d667657 --- /dev/null +++ b/kademlia/tests/test_utils.py @@ -0,0 +1,37 @@ +import hashlib + +from twisted.trial import unittest + +from kademlia.utils import digest, sharedPrefix, OrderedSet + + +class UtilsTest(unittest.TestCase): + def test_digest(self): + d = hashlib.sha1('1').digest() + self.assertEqual(d, digest(1)) + + d = hashlib.sha1('another').digest() + self.assertEqual(d, digest('another')) + + def test_sharedPrefix(self): + args = ['prefix', 'prefixasdf', 'prefix', 'prefixxxx'] + self.assertEqual(sharedPrefix(args), 'prefix') + + args = ['p', 'prefixasdf', 'prefix', 'prefixxxx'] + self.assertEqual(sharedPrefix(args), 'p') + + args = ['one', 'two'] + self.assertEqual(sharedPrefix(args), '') + + args = ['hi'] + self.assertEqual(sharedPrefix(args), 'hi') + + +class OrderedSetTest(unittest.TestCase): + def test_order(self): + o = OrderedSet() + o.push('1') + o.push('1') + o.push('2') + o.push('1') + self.assertEqual(o, ['2', '1']) diff --git a/kademlia/utils.py b/kademlia/utils.py index 43e342f..63209e8 100644 --- a/kademlia/utils.py +++ b/kademlia/utils.py @@ -15,13 +15,15 @@ def digest(s): def deferredDict(d): """ - Just like a C{defer.DeferredList} but instead accepts and returns a C{dict}. + Just like a :class:`defer.DeferredList` but instead accepts and returns a :class:`dict`. - @param d: A C{dict} whose values are all C{Deferred} objects. + Args: + d: A :class:`dict` whose values are all :class:`defer.Deferred` objects. - @return: A C{DeferredList} whose callback will be given a dictionary whose - keys are the same as the parameter C{d}'s and whose values are the results - of each individual deferred call. + Returns: + :class:`defer.DeferredList` whose callback will be given a dictionary whose + keys are the same as the parameter :obj:`d` and whose values are the results + of each individual deferred call. """ if len(d) == 0: return defer.succeed({}) @@ -37,7 +39,15 @@ def deferredDict(d): class OrderedSet(list): + """ + Acts like a list in all ways, except in the behavior of the :meth:`push` method. + """ + def push(self, thing): + """ + 1. If the item exists in the list, it's removed + 2. The item is pushed to the end of the list + """ if thing in self: self.remove(thing) self.append(thing) @@ -47,8 +57,11 @@ def sharedPrefix(args): """ Find the shared prefix between the strings. - For instance, sharedPrefix(['blahblah', 'blahwhat']) is - 'blah'. + For instance: + + sharedPrefix(['blahblah', 'blahwhat']) + + returns 'blah'. """ i = 0 while i < min(map(len, args)):