summaryrefslogtreecommitdiff
path: root/kademlia/datastore.py
diff options
context:
space:
mode:
authorYuval Adam <yuv.adm@gmail.com>2012-01-17 11:04:05 +0200
committerYuval Adam <yuv.adm@gmail.com>2012-01-17 11:04:05 +0200
commitea59a72d6f6d31c53b95531dd9700bdd13a2fafc (patch)
tree9724c99c1747ab1aed6c88f7a1a118cfc18d4b43 /kademlia/datastore.py
parent984794de2eaa8de85dd91aa4a3fcdf7045226f4b (diff)
initial kademlia files
Diffstat (limited to 'kademlia/datastore.py')
-rw-r--r--kademlia/datastore.py183
1 files changed, 183 insertions, 0 deletions
diff --git a/kademlia/datastore.py b/kademlia/datastore.py
new file mode 100644
index 0000000..45633e1
--- /dev/null
+++ b/kademlia/datastore.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+#
+# This library is free software, distributed under the terms of
+# the GNU Lesser General Public License Version 3, or any later version.
+# See the COPYING file included in this archive
+#
+# The docstrings in this module contain epytext markup; API documentation
+# may be created by processing this file with epydoc: http://epydoc.sf.net
+
+import UserDict
+import sqlite3
+import cPickle as pickle
+import time
+import os
+
+
+class DataStore(UserDict.DictMixin):
+ """ Interface for classes implementing physical storage (for data
+ published via the "STORE" RPC) for the Kademlia DHT
+
+ @note: This provides an interface for a dict-like object
+ """
+ def keys(self):
+ """ Return a list of the keys in this data store """
+
+ def lastPublished(self, key):
+ """ Get the time the C{(key, value)} pair identified by C{key}
+ was last published """
+
+ def originalPublisherID(self, key):
+ """ Get the original publisher of the data's node ID
+
+ @param key: The key that identifies the stored data
+ @type key: str
+
+ @return: Return the node ID of the original publisher of the
+ C{(key, value)} pair identified by C{key}.
+ """
+
+ def originalPublishTime(self, key):
+ """ Get the time the C{(key, value)} pair identified by C{key}
+ was originally published """
+
+ def setItem(self, key, value, lastPublished, originallyPublished, originalPublisherID):
+ """ Set the value of the (key, value) pair identified by C{key};
+ this should set the "last published" value for the (key, value)
+ pair to the current time
+ """
+
+ def __getitem__(self, key):
+ """ Get the value identified by C{key} """
+
+ def __setitem__(self, key, value):
+ """ Convenience wrapper to C{setItem}; this accepts a tuple in the
+ format: (value, lastPublished, originallyPublished, originalPublisherID) """
+ self.setItem(key, *value)
+
+ def __delitem__(self, key):
+ """ Delete the specified key (and its value) """
+
+class DictDataStore(DataStore):
+ """ A datastore using an in-memory Python dictionary """
+ def __init__(self):
+ # Dictionary format:
+ # { <key>: (<value>, <lastPublished>, <originallyPublished> <originalPublisherID>) }
+ self._dict = {}
+
+ def keys(self):
+ """ Return a list of the keys in this data store """
+ return self._dict.keys()
+
+ def lastPublished(self, key):
+ """ Get the time the C{(key, value)} pair identified by C{key}
+ was last published """
+ return self._dict[key][1]
+
+ def originalPublisherID(self, key):
+ """ Get the original publisher of the data's node ID
+
+ @param key: The key that identifies the stored data
+ @type key: str
+
+ @return: Return the node ID of the original publisher of the
+ C{(key, value)} pair identified by C{key}.
+ """
+ return self._dict[key][3]
+
+ def originalPublishTime(self, key):
+ """ Get the time the C{(key, value)} pair identified by C{key}
+ was originally published """
+ return self._dict[key][2]
+
+ def setItem(self, key, value, lastPublished, originallyPublished, originalPublisherID):
+ """ Set the value of the (key, value) pair identified by C{key};
+ this should set the "last published" value for the (key, value)
+ pair to the current time
+ """
+ self._dict[key] = (value, lastPublished, originallyPublished, originalPublisherID)
+
+ def __getitem__(self, key):
+ """ Get the value identified by C{key} """
+ return self._dict[key][0]
+
+ def __delitem__(self, key):
+ """ Delete the specified key (and its value) """
+ del self._dict[key]
+
+
+class SQLiteDataStore(DataStore):
+ """ Example of a SQLite database-based datastore
+ """
+ def __init__(self, dbFile=':memory:'):
+ """
+ @param dbFile: The name of the file containing the SQLite database; if
+ unspecified, an in-memory database is used.
+ @type dbFile: str
+ """
+ createDB = not os.path.exists(dbFile)
+ self._db = sqlite3.connect(dbFile)
+ self._db.isolation_level = None
+ self._db.text_factory = str
+ if createDB:
+ self._db.execute('CREATE TABLE data(key, value, lastPublished, originallyPublished, originalPublisherID)')
+ self._cursor = self._db.cursor()
+
+ def keys(self):
+ """ Return a list of the keys in this data store """
+ keys = []
+ try:
+ self._cursor.execute("SELECT key FROM data")
+ for row in self._cursor:
+ keys.append(row[0].decode('hex'))
+ finally:
+ return keys
+
+ def lastPublished(self, key):
+ """ Get the time the C{(key, value)} pair identified by C{key}
+ was last published """
+ return int(self._dbQuery(key, 'lastPublished'))
+
+ def originalPublisherID(self, key):
+ """ Get the original publisher of the data's node ID
+
+ @param key: The key that identifies the stored data
+ @type key: str
+
+ @return: Return the node ID of the original publisher of the
+ C{(key, value)} pair identified by C{key}.
+ """
+ return self._dbQuery(key, 'originalPublisherID')
+
+ def originalPublishTime(self, key):
+ """ Get the time the C{(key, value)} pair identified by C{key}
+ was originally published """
+ return int(self._dbQuery(key, 'originallyPublished'))
+
+ def setItem(self, key, value, lastPublished, originallyPublished, originalPublisherID):
+ # Encode the key so that it doesn't corrupt the database
+ encodedKey = key.encode('hex')
+ self._cursor.execute("select key from data where key=:reqKey", {'reqKey': encodedKey})
+ if self._cursor.fetchone() == None:
+ self._cursor.execute('INSERT INTO data(key, value, lastPublished, originallyPublished, originalPublisherID) VALUES (?, ?, ?, ?, ?)', (encodedKey, buffer(pickle.dumps(value, pickle.HIGHEST_PROTOCOL)), lastPublished, originallyPublished, originalPublisherID))
+ else:
+ self._cursor.execute('UPDATE data SET value=?, lastPublished=?, originallyPublished=?, originalPublisherID=? WHERE key=?', (buffer(pickle.dumps(value, pickle.HIGHEST_PROTOCOL)), lastPublished, originallyPublished, originalPublisherID, encodedKey))
+
+ def _dbQuery(self, key, columnName, unpickle=False):
+ try:
+ self._cursor.execute("SELECT %s FROM data WHERE key=:reqKey" % columnName, {'reqKey': key.encode('hex')})
+ row = self._cursor.fetchone()
+ value = str(row[0])
+ except TypeError:
+ raise KeyError, key
+ else:
+ if unpickle:
+ return pickle.loads(value)
+ else:
+ return value
+
+ def __getitem__(self, key):
+ return self._dbQuery(key, 'value', unpickle=True)
+
+ def __delitem__(self, key):
+ self._cursor.execute("DELETE FROM data WHERE key=:reqKey", {'reqKey': key.encode('hex')})