Changeset: 43cb06902e1f for MonetDB URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=43cb06902e1f Added Files: clients/python/monetdb/control.py clients/python/test/control.py Modified Files: clients/python/monetdb/mapi.py Branch: default Log Message:
we can now manage monetdb databases from python. diffs (222 lines): diff --git a/clients/python/monetdb/control.py b/clients/python/monetdb/control.py new file mode 100644 --- /dev/null +++ b/clients/python/monetdb/control.py @@ -0,0 +1,54 @@ + +from monetdb import mapi + +class Control: + def __init__(self, hostname, port, passphrase): + self.server = mapi.Server() + self.server.connect(hostname, port, 'monetdb', passphrase, 'merovingian', 'control') + + def _send_command(self, database_name, command, has_output=False): + return self.server.cmd("%s %s\n" % (database_name, command)) + + def create(self, database_name): + return self._send_command(database_name, "create") + + def destroy(self, database_name): + return self._send_command(database_name, "destroy") + + def lock(self, database_name): + return self._send_command(database_name, "lock") + + def release(self, database_name): + return self._send_command(database_name, "release") + + def status(self, database_name): + return self._send_command(database_name, "status", True) + + def start(self, database_name): + return self._send_command(database_name, "start") + + def stop(self, database_name): + return self._send_command(database_name, "stop") + + def kill(self, database_name): + return self._send_command(database_name, "kill") + + def set(self, database_name, property_, value): + return self._send_command(database_name, "%s=%s" % (property_, value)) + + def get(self, database_name): + properties = self._send_command(database_name, "get") + values = {} + for dirty_line in properties.split("\n")[1:]: + line = dirty_line[1:] + if not line.startswith("#"): + if "=" in line: + split = line.split("=") + values[split[0]] = split[1] + return values + + def inherit(self, database_name, property_): + return self._send_command(database_name, property_ + "=") + + def version(self, database_name): + self._send_command(database_name, "version") \ No newline at end of file diff --git a/clients/python/monetdb/mapi.py b/clients/python/monetdb/mapi.py --- a/clients/python/monetdb/mapi.py +++ b/clients/python/monetdb/mapi.py @@ -24,6 +24,7 @@ import logging import struct import hashlib from cStringIO import StringIO +import time from monetdb.exceptions import OperationalError, DatabaseError, ProgrammingError, NotSupportedError @@ -45,6 +46,7 @@ MSG_QBLOCK = "&6" MSG_HEADER = "%" MSG_TUPLE = "[" MSG_REDIRECT = "^" +MSG_OK = "=OK" STATE_INIT = 0 STATE_READY = 1 @@ -100,9 +102,12 @@ class Server(object): self.__putblock(response) prompt = self.__getblock().strip() - if len(prompt) == 0: + if len(prompt) == 0 : # Empty response, server is happy pass + elif prompt == MSG_OK: + # New behaviour, I think it is something good + pass elif prompt.startswith(MSG_INFO): logger.info("II %s" % prompt[1:]) @@ -134,11 +139,9 @@ class Server(object): self.password, self.database, self.language) else: - logger.error('!' + prompt[0]) raise ProgrammingError("unknown redirect: %s" % prompt) else: - logger.error('!' + prompt[0]) raise ProgrammingError("unknown state: %s" % prompt) self.state = STATE_READY @@ -161,7 +164,10 @@ class Server(object): self.__putblock(operation) response = self.__getblock() if not len(response): - return + return True + elif response.startswith(MSG_OK): + # New behaviour, I think it is something good + return response.strip() if response == MSG_MORE: # tell server it isn't going to get more return self.cmd("") @@ -233,6 +239,8 @@ class Server(object): while count > 0: try: recv = self.socket.recv(bytes) + if len(recv) == 0: + time.sleep(1) logger.debug("II: package size: %i payload: %s" % (len(recv), recv)) except socket.error, error: raise OperationalError(error[1]) diff --git a/clients/python/test/control.py b/clients/python/test/control.py new file mode 100644 --- /dev/null +++ b/clients/python/test/control.py @@ -0,0 +1,90 @@ +import unittest +from monetdb.control import Control +from monetdb.exceptions import OperationalError +import logging +logging.basicConfig(level=logging.DEBUG) + +database_prefix = 'controltest_' +database_name = database_prefix + 'other' +passphrase = 'testdb' + +def do_without_fail(function): + try: + function() + except OperationalError: + pass + +class TestManage(unittest.TestCase): + def setUp(self): + self.control = Control('localhost', 50000, passphrase) + do_without_fail(lambda: self.control.stop(database_name)) + do_without_fail(lambda: self.control.destroy(database_name)) + self.control.create(database_name) + + def tearDown(self): + do_without_fail(lambda: self.control.stop(database_name)) + do_without_fail(lambda: self.control.destroy(database_name)) + + def testCreate(self): + create_name = database_prefix + "create" + do_without_fail(lambda: self.control.destroy(create_name)) + + self.control.create(create_name) + # can't create it again + self.assertRaises(OperationalError, self.control.create, create_name) + + # cleanup + do_without_fail(lambda: self.control.destroy(create_name)) + + def testDestroy(self): + destroy_name = database_prefix + "destroy" + self.control.create(destroy_name) + self.control.destroy(destroy_name) + self.assertRaises(OperationalError, self.control.destroy, destroy_name) + + def testLock(self): + do_without_fail(lambda: self.control.release(database_name)) + self.control.lock(database_name) + self.assertRaises(OperationalError, self.control.lock, database_name) + + def testRelease(self): + do_without_fail(lambda: self.control.release(database_name)) + do_without_fail(lambda: self.control.lock(database_name)) + self.assertTrue(self.control.release(database_name)) + self.assertRaises(OperationalError, self.control.release, database_name) + + def testStatus(self): + self.control.status(database_name) + + def testStart(self): + do_without_fail(lambda: self.control.stop(database_name)) + self.assertTrue(self.control.start(database_name)) + + def testStop(self): + do_without_fail(lambda: self.control.start(database_name)) + self.assertTrue(self.control.stop(database_name)) + + def testKill(self): + do_without_fail(lambda: self.control.start(database_name)) + self.assertTrue(self.control.kill(database_name)) + + def testSet(self): + property_ = "readonly" + value = "yes" + self.control.set(database_name, property_, value) + properties = self.control.get(database_name) + self.assertEqual(properties[property_], value) + + def testGet(self): + properties = self.control.get(database_name) + + def testInherit(self): + self.control.set(database_name, "readonly", "yes") + self.assertTrue(self.control.inherit(database_name, "readonly")) + self.assertFalse(self.control.get(database_name).has_key("readonly")) + + def testVersion(self): + self.control.version(database_name) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file _______________________________________________ Checkin-list mailing list Checkin-list@monetdb.org http://mail.monetdb.org/mailman/listinfo/checkin-list