Introduction
TinyDB provides JSONStorage, MemoryStorage and Middleware when initializing a database. This article will analyze the code about the properties of database.
database.py
In the tinydb.py , Class TinyDB is the subclass of Table or Object
from .utils import with_typehint
from .table import Table
TableBase: Type[Table] = with_typehint(Table)
class TinyDB(TableBase):
...
The with_typehint function is in utils.py defined as
from typing import TYPE_CHECKING
def with_typehint(baseclass: Type[T]):
if TYPE_CHEKING:
return baseclass
return object
Therefore, with_typehint actually pass the baseclass to Class TinyDB, or simply let TinyDB initialized like an ordinary class with object as its type hint if an import cirle occurs, checked by TYPE_CHECKING.
table.py
The Class Table is in table.py, and its __init__ method is defined as
def __init__(
self,
storage: Storage,
name: str,
cache_size: int = default_query_cache_capacity
):
...
The cache_size will be used to initialize a LRUCache, which is defined in utils.py.
from collections import OrderedDict, abc
from typing import TypeVar, Generic
K = TypeVar('K')
V = TypeVar('V')
class LRUCache(abc.MutableMapping, Generic[K, V]):
def __init__(self, capacity=None) -> None:
self.capacity = capacity
self.cache: OrderedDict[K, V] = OrderedDict()
...
abc is the abbreviation of Abstract Base Class, which is used to create abstract classes which cannot be used directly but can generate subclasses. Therefore, the subclasses must implement some of the methods the abstract class declared.
storages.py
Storage is used in initializing Table, which is also an ABC example.
class Storage(ABC):
@abstractmethod
def read(self) -> Optional[Dict[str, Dict[str, Any]]]:
raise NotImplementedError('To be overridden!')
...
When using ABC, The @abstractmethod decorator is used to indicate that this is an abstract method and that it must be implemented by any subclass of Storage.
From Storage, it generates JSONStorage and MemoryStorage.
class JSONStorage(Storage):
def __init__(self, path: str, create_dirs=False, encoding=None, access_mode='r+', **kwargs):
...
...
class MemoryStorage(Storage):
def __init__(self):
...
...
middlewares.py
In middlewares.py, it defines a class Middleware.
class Middleware:
def __init__(self, storage_cls) -> None:
self._storage_cls = storage_cls
self.storage: Storage = None
From this class, the code gives an example - CachingMiddleware.
class CachingMiddleware(Middleware):
def __init__(self, storage_cls):
super().__init__(sorage_cls)
self.cache = None
self._cache_modified_count = 0
...
Except for __init__, it also implements read, write and close. They are essential because the instance of CachingMiddleware should replace the original Storage.