diff --git a/kvlite.py b/kvlite.py index a79f7ec..4dab633 100644 --- a/kvlite.py +++ b/kvlite.py @@ -38,14 +38,13 @@ POSSIBILITY OF SUCH DAMAGE.""" import os -import cmd import sys import json import zlib import uuid -import pprint import sqlite3 import binascii +import cPickle as pickle __all__ = ['open', 'remove',] @@ -55,10 +54,7 @@ print >> sys.stderr, 'Error! MySQLdb package is not installed, please install python-mysqldb' sys.exit() -try: - import cPickle as pickle -except ImportError: - import pickle + # TODO add test cases for serializers # TODO add support user specific serializators @@ -95,8 +91,6 @@ def dumps(v): @staticmethod def loads(v): ''' loads value ''' - if isinstance(v, unicode): - v = str(v) return pickle.loads(v) # ----------------------------------------------------------------- @@ -106,6 +100,8 @@ def loads(v): class CompressedJsonSerializer(object): ''' CompressedJsonSerializer ''' + # TODO issue: sqlite doesn't support binary values + @staticmethod def dumps(v): ''' dumps value ''' @@ -155,16 +151,10 @@ def remove(uri): ''' remove collection by URI ''' - backend, rest_uri = uri.split('://') - if backend in SUPPORTED_BACKENDS: - if backend == 'mysql': - MysqlCollection(uri).remove() - elif backend == 'sqlite': - SqliteCollection(uri).remove() - else: - raise NotImplementedError('unknown backend: {}'.format(uri)) - else: - raise RuntimeError('Unknown backend: {}'.format(backend)) + manager = CollectionManager(uri) + params = manager.parse_uri(uri) + if params['collection'] in manager.collections(): + manager.remove(params['collection']) def get_uuid(amount=100): ''' return UUIDs ''' @@ -364,18 +354,15 @@ def get(self, k=None): ''' if k: if len(k) > 40: - raise WronKeyValue() + raise RuntimeError('The length of key is more than 40 bytes') SQL = 'SELECT k,v FROM %s WHERE k = ' % self.__collection try: self.__cursor.execute(SQL + "%s", binascii.a2b_hex(k)) - except TypeError, err: - raise WronKeyValue(err) + except Exception, err: + raise RuntimeError(err) result = self.__cursor.fetchone() if result: - try: - v = self.__serializer.loads(result[1]) - except Exception, err: - raise RuntimeError('key %s, %s' % (k, err)) + v = self.__serializer.loads(result[1]) return (binascii.b2a_hex(result[0]), v) else: return (None, None) @@ -392,20 +379,15 @@ def put(self, k, v): SQL_INSERT = 'INSERT INTO %s (k,v) ' % self.__collection SQL_INSERT += 'VALUES (%s,%s) ON DUPLICATE KEY UPDATE v=%s;;' v = self.__serializer.dumps(v) - try: - self.__cursor.execute(SQL_INSERT, (binascii.a2b_hex(k), v, v)) - except TypeError, err: - raise RuntimeError(err) + self.__cursor.execute(SQL_INSERT, (binascii.a2b_hex(k), v, v)) def delete(self, k): ''' delete document by k ''' if len(k) > 40: - raise WronKeyValue() + raise RuntimeError('The length of key is more than 40 bytes') + SQL_DELETE = '''DELETE FROM %s WHERE k = ''' % self.__collection - try: - self.__cursor.execute(SQL_DELETE + "%s;", binascii.a2b_hex(k)) - except TypeError, err: - raise WronKeyValue(err) + self.__cursor.execute(SQL_DELETE + "%s;", binascii.a2b_hex(k)) def keys(self): ''' return document keys in collection''' @@ -445,6 +427,7 @@ def __init__(self, uri): self.__conn = sqlite3.connect(params['db']) self.__cursor = self.__conn.cursor() + self.__conn.text_factory = str @staticmethod def parse_uri(uri): @@ -533,10 +516,7 @@ def put(self, k, v): SQL_INSERT = 'INSERT OR REPLACE INTO %s (k,v) ' % self.__collection SQL_INSERT += 'VALUES (?,?)' v = self.__serializer.dumps(v) - try: - self.__cursor.execute(SQL_INSERT, (k, v)) - except TypeError, err: - raise RuntimeError(err) + self.__cursor.execute(SQL_INSERT, (k, v)) def __get_many(self): ''' return all docs ''' @@ -567,8 +547,8 @@ def get(self, k=None): SQL = 'SELECT k,v FROM %s WHERE k = ?;' % self.__collection try: self.__cursor.execute(SQL, (k,)) - except TypeError, err: - raise WronKeyValue(err) + except Exception, err: + raise RuntimeError(err) result = self.__cursor.fetchone() if result: try: @@ -599,10 +579,7 @@ def delete(self, k): if len(k) > 40: raise RuntimeError('The key length is more than 40 bytes') SQL_DELETE = '''DELETE FROM %s WHERE k = ?;''' % self.__collection - try: - self.__cursor.execute(SQL_DELETE, (k,)) - except TypeError, err: - raise WronKeyValue(err) + self.__cursor.execute(SQL_DELETE, (k,)) @property def count(self): @@ -617,265 +594,4 @@ def close(self): ''' close connection to database ''' self.__conn.close() -# ----------------------------------------------------------------- -# Console class -# ----------------------------------------------------------------- -class Console(cmd.Cmd): - def __init__(self): - cmd.Cmd.__init__(self) - self.prompt = "kvlite> " - self.ruler = '-' - - self.__history_size = 20 - self.__history = list() - self.__kvlite_colls = dict() - self.__current_coll_name = 'kvlite' - self.__current_coll = None - - def emptyline(self): - return False - - def do_help(self, arg): - ''' help \tshow help''' - if arg: - try: - func = getattr(self, 'help_' + arg) - except AttributeError: - try: - doc=getattr(self, 'do_' + arg).__doc__ - if doc: - self.stdout.write("%s\n"%str(doc)) - return - except AttributeError: - pass - self.stdout.write("%s\n"%str(self.nohelp % (arg,))) - return - else: - names = [ - '', 'do_help', 'do_version', 'do_licence', 'do_history', 'do_exit', '', - 'do_create', 'do_use', 'do_show', 'do_remove', 'do_import', 'do_export', '', - 'do_hash', 'do_keys', 'do_items', 'do_get', 'do_put', 'do_delete', 'do_count', '' - ] - for name in names: - if not name: - print - else: - print getattr(self, name).__doc__ - - def do_history(self,line): - ''' history\t\tshow commands history ''' - for i, line in enumerate(self.__history): - print "0%d. %s" % (i+1, line) - - def precmd(self, line): - if len(self.__history) == self.__history_size: - prev_line = self.__history.pop(0) - if line and line not in self.__history: - self.__history.append(line) - return line - - def do_version(self, line): - ''' version\t\tshow kvlite version''' - print 'version: %s' % __version__ - - def do_licence(self, line): - ''' licence\t\tshow licence''' - print __license__ - print - - def do_exit(self, line): - ''' exit\t\t\texit from console ''' - return True - - def do_import(self, filename): - ''' import \timport collection configuration from JSON file''' - import os - - if not filename: - print getattr(self, 'do_import').__doc__ - return - filename = filename.rstrip().lstrip() - - if os.path.isfile(filename): - for k, v in json_decode(open(filename).read()).items(): - self.__kvlite_colls[k] = v - print 'Import completed' - else: - print 'Error! File %s does not exists' % filename - - def do_export(self, filename): - ''' export \texport collection configurations to JSON file''' - # TODO check if file exists. If yes, import about it - if not filename: - print getattr(self, 'do_import').__doc__ - return - filename = filename.rstrip().lstrip() - json_file = open(filename, 'w') - json_file.write(json_encode(self.__kvlite_colls)) - json_file.close() - print 'Export completed to file: %s' % filename - - def do_show(self, line): - ''' show collections\tlist of available collections (defined in settings.py)''' - if line == 'collections': - for coll in self.__kvlite_colls: - print ' %s' % coll - else: - print 'Unknown argument: %s' % line - - def do_use(self, collection_name): - ''' use \tuse the collection as the default (current) collection''' - if collection_name in self.__kvlite_colls: - self.prompt = '%s>' % collection_name - self.__current_coll_name = collection_name - self.__current_coll = Collection(self.__kvlite_colls[self.__current_coll_name]) - return - else: - print 'Error! Unknown collection: %s' % collection_name - - def do_create(self, line): - ''' create \tcreate new collection (if not exists)''' - try: - name, uri = [i for i in line.split(' ') if i <> ''] - except ValueError: - print getattr(self, 'do_create').__doc__ - return - - if name in self.__kvlite_colls: - print 'Warning! Collection name already defined: %s, %s' % (name, self.__kvlite_colls[name]) - print 'If needed please change collection name' - return - try: - if is_collection_exists(uri): - self.__kvlite_colls[name] = uri - print 'Connection exists, the reference added to collection list' - return - else: - create_collection(uri) - self.__kvlite_colls[name] = uri - print 'Collection created and added to collection list' - return - except WrongURIException: - print 'Error! Incorrect URI' - return - except ConnectionError, err: - print 'Connection Error! Please check URI, %s' % str(err) - return - - def do_remove(self, name): - ''' remove \tremove collection''' - if name not in self.__kvlite_colls: - print 'Error! Collection name does not exist: %s' % name - return - try: - if is_collection_exists(self.__kvlite_colls[name]): - delete_collection(self.__kvlite_colls[name]) - del self.__kvlite_colls[name] - print 'Collection %s deleted' % name - return - else: - print 'Error! Collection does not exist, %s' % self.__kvlite_colls[name] - except WrongURIException: - print 'Error! Incorrect URI' - return - except ConnectionError, err: - print 'Connection Error! Please check URI, %s' % str(err) - return - - def do_hash(self, line): - ''' hash [string]\tgenerate sha1 hash, random if string is not defined''' - import hashlib - import datetime - if not line: - str_now = str(datetime.datetime.now()) - print 'Random sha1 hash:', hashlib.sha1(str_now).hexdigest() - else: - line = line.rstrip().lstrip() - print 'sha1 hash:', hashlib.sha1(line).hexdigest() - - def do_keys(self, line): - ''' keys\t\t\tlist of keys ''' - if not self.__current_coll_name in self.__kvlite_colls: - print 'Error! Unknown collection: %s' % self.__current_coll_name - return - for k,v in self.__current_coll.get(): - print k - - def do_items(self, line): - ''' items\t\tlist of collection's items ''' - if not self.__current_coll_name in self.__kvlite_colls: - print 'Error! Unknown collection: %s' % self.__current_coll_name - return - for k,v in self.__current_coll.get(): - print k - pprint.pprint(v) - print - - def do_count(self, args): - ''' count\t\tshow the amount of entries in collection ''' - if self.__current_coll: - print self.__current_coll.count() - - def do_get(self, key): - ''' get \t\tshow collection entry by key''' - if not key: - print getattr(self, 'do_get').__doc__ - return - for key in [k for k in key.split(' ') if k <> '']: - if self.__current_coll: - k, v = self.__current_coll.get(key) - print k - pprint.pprint(v) - print - else: - print 'Error! The collection is not selected, please use collection' - return - - def do_put(self, line): - ''' put \tstore entry to collection''' - try: - k,v = [i for i in line.split(' ',1) if i <> ''] - except ValueError: - print getattr(self, 'do_put').__doc__ - return - - try: - v = json_decode(v) - except ValueError, err: - print 'Value decoding error!', err - return - - if self.__current_coll: - try: - self.__current_coll.put(k, v) - self.__current_coll.commit() - print 'Done' - return - except WronKeyValue, err: - print 'Error! Incorrect key/value,', err - return - else: - print 'Error! The collection is not selected, please use collection' - return - - - def do_delete(self, key): - ''' delete \t\tdelete entry by key ''' - key = key.rstrip().lstrip() - if self.__current_coll.get(key) <> (None, None): - self.__current_coll.delete(key) - self.__current_coll.commit() - print 'Done' - return - else: - print 'Error! The key %s does not exist' % key - return - -# ---------------------------------- -# main -# ---------------------------------- -if __name__ == '__main__': - console = Console() - console.cmdloop() -