#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
# vim: se ts=4 et syn=python:
# created by: matteo.guadrini
# odm -- nosqlapi
#
# Copyright (C) 2022 Matteo Guadrini <matteo.guadrini@hotmail.it>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""ODM module for key-value NOSQL database."""
# region Imports
from collections import namedtuple
# endregion
# region global variable
__all__ = ['Keyspace', 'Subspace', 'Transaction', 'Item', 'ExpiredItem', 'Index']
# endregion
# region Classes
[docs]class Transaction:
"""Represents group of commands in a single step"""
[docs] def __init__(self, commands=None):
"""Transaction object
:param commands: List of commands
"""
if commands is None:
commands = []
self._commands = commands
@property
def commands(self):
"""Command list"""
return list(enumerate(self._commands))
[docs] def add(self, command, index=-1):
"""Add command to commands list
:param command: Command string
:param index: Index to append command
:return: None
"""
if index == -1:
self._commands.append(command)
else:
self._commands.insert(index, command)
[docs] def delete(self, index=-1):
"""Remove command to command list
:param index: Index to remove command
:return: None
"""
self._commands.pop(index)
def __getitem__(self, item):
return self._commands[item]
def __setitem__(self, key, value):
self._commands[key] = value
def __delitem__(self, key):
del self._commands[key]
[docs] def __repr__(self):
return f'<{self.__class__.__name__} object, commands={len(self.commands)}>'
[docs] def __str__(self):
return '\n'.join(command for command in self._commands)
def __iter__(self):
return (command for command in self._commands)
[docs]class Keyspace:
"""Represents keyspace like database"""
[docs] def __init__(self, name, exists=False):
"""Keyspace object
:param name: Name of keyspace
:param exists: Existing keyspace (default False)
"""
self._name = name
self._exists = exists
self._store = []
@property
def name(self):
"""Name of keyspace"""
return self._name
@name.setter
def name(self, value):
"""Name of keyspace"""
self._name = value
@property
def exists(self):
"""Existence of keyspace"""
return self._exists
@property
def store(self):
"""List of object into keyspace"""
return self._store
[docs] def append(self, item):
"""Append item into store
:param item: Key/value item
:return: None
"""
self._store.append(item)
[docs] def pop(self, item=-1):
"""Remove item from the store
:param item: Index of item to remove
:return: None
"""
self._store.pop(item)
def __getitem__(self, item):
return self.store[item]
def __setitem__(self, key, value):
self._store[key] = value
def __delitem__(self, key):
del self._store[key]
[docs] def __repr__(self):
return f'<{self.__class__.__name__} object, name={self.name}>'
[docs] def __str__(self):
return f'{self.store}'
def __len__(self):
return len(self.store)
def __iter__(self):
return (obj for obj in self._store)
[docs]class Subspace(Keyspace):
"""Represents subspace of the keyspace"""
[docs] def __init__(self, name, sub=None, sep='.'):
"""Subspace object
:param name: Name of keyspace
:param sub: Subname of keyspace
:param sep: Separation character
"""
super().__init__(name)
if sub:
self.name += sep + sub
[docs]class Item:
"""Represents key/value like a dictionary"""
[docs] def __init__(self, key, value=None):
"""Item object
:param key: Key of item
:param value: Value of item
"""
self._key = key
self._value = value
self.__dict = {}
self.set(key, value)
@property
def key(self):
"""Key of item"""
return self._key
@property
def value(self):
"""Value of the key"""
return self._value
[docs] def get(self):
"""Get item
:return: dict
"""
return self.__dict
[docs] def set(self, key, value=None):
"""Set item
:param key: Key of item
:param value: Value of the key
:return: None
"""
self[key] = value
def __getitem__(self, item):
return self.__dict.get(item)
def __setitem__(self, key, value):
if not self.__dict.get(key):
self.__dict.clear()
self.__dict[key] = value
self._key = key
self._value = value
def __delitem__(self, key):
del self.__dict[key]
[docs] def __repr__(self):
return f'<{self.__class__.__name__} object, key={self.key} value={self.value}>'
[docs] def __str__(self):
return f'{self.__dict}'
[docs]class ExpiredItem(Item):
"""Represents Item object with ttl expired time"""
[docs] def __init__(self, key, value=None, ttl=None):
"""ExpiredItem object
:param key: Key of item
:param value: Value of item
:param ttl: Time to live of item
"""
super().__init__(key, value)
self._ttl = ttl
self['ttl'] = self._ttl
@property
def ttl(self):
"""Time to live of item"""
return self._ttl
def __setitem__(self, key, value):
if key == 'ttl':
self._ttl = value
self['ttl'] = self._ttl
else:
if not self.__dict.get(key):
self.__dict.clear()
self.__dict[key] = value
self._key = key
self._value = value
self.__dict['ttl'] = self._ttl
[docs] def __repr__(self):
return f'<{self.__class__.__name__} object, key={self.key} value={self.value} ttl={self.ttl}>'
# endregion
# region Other objects
Index = namedtuple('Index', ['name', 'key'])
# endregion