middlewares.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. """
  2. Contains the :class:`base class <tinydb.middlewares.Middleware>` for
  3. middlewares and implementations.
  4. """
  5. from . import TinyDB
  6. class Middleware(object):
  7. """
  8. The base class for all Middlewares.
  9. Middlewares hook into the read/write process of TinyDB allowing you to
  10. extend the behaviour by adding caching, logging, ...
  11. Your middleware's ``__init__`` method has to accept exactly one
  12. argument which is the class of the "real" storage. It has to be stored as
  13. ``_storage_cls`` (see :class:`~tinydb.middlewares.CachingMiddleware` for an
  14. example).
  15. """
  16. def __init__(self, storage_cls=TinyDB.DEFAULT_STORAGE):
  17. self._storage_cls = storage_cls
  18. self.storage = None
  19. def __call__(self, *args, **kwargs):
  20. """
  21. Create the storage instance and store it as self.storage.
  22. Usually a user creates a new TinyDB instance like this::
  23. TinyDB(storage=StorageClass)
  24. The storage kwarg is used by TinyDB this way::
  25. self.storage = storage(*args, **kwargs)
  26. As we can see, ``storage(...)`` runs the constructor and returns the
  27. new storage instance.
  28. Using Middlewares, the user will call::
  29. The 'real' storage class
  30. v
  31. TinyDB(storage=Middleware(StorageClass))
  32. ^
  33. Already an instance!
  34. So, when running ``self.storage = storage(*args, **kwargs)`` Python
  35. now will call ``__call__`` and TinyDB will expect the return value to
  36. be the storage (or Middleware) instance. Returning the instance is
  37. simple, but we also got the underlying (*real*) StorageClass as an
  38. __init__ argument that still is not an instance.
  39. So, we initialize it in __call__ forwarding any arguments we recieve
  40. from TinyDB (``TinyDB(arg1, kwarg1=value, storage=...)``).
  41. In case of nested Middlewares, calling the instance as if it was an
  42. class results in calling ``__call__`` what initializes the next
  43. nested Middleware that itself will initialize the next Middleware and
  44. so on.
  45. """
  46. self.storage = self._storage_cls(*args, **kwargs)
  47. return self
  48. def __getattr__(self, name):
  49. """
  50. Forward all unknown attribute calls to the underlying storage so we
  51. remain as transparent as possible.
  52. """
  53. return getattr(self.__dict__['storage'], name)
  54. class CachingMiddleware(Middleware):
  55. """
  56. Add some caching to TinyDB.
  57. This Middleware aims to improve the performance of TinyDB by writing only
  58. the last DB state every :attr:`WRITE_CACHE_SIZE` time and reading always
  59. from cache.
  60. """
  61. #: The number of write operations to cache before writing to disc
  62. WRITE_CACHE_SIZE = 1000
  63. def __init__(self, storage_cls=TinyDB.DEFAULT_STORAGE):
  64. super(CachingMiddleware, self).__init__(storage_cls)
  65. self.cache = None
  66. self._cache_modified_count = 0
  67. def read(self):
  68. if self.cache is None:
  69. self.cache = self.storage.read()
  70. return self.cache
  71. def write(self, data):
  72. self.cache = data
  73. self._cache_modified_count += 1
  74. if self._cache_modified_count >= self.WRITE_CACHE_SIZE:
  75. self.flush()
  76. def flush(self):
  77. """
  78. Flush all unwritten data to disk.
  79. """
  80. if self._cache_modified_count > 0:
  81. self.storage.write(self.cache)
  82. self._cache_modified_count = 0
  83. def close(self):
  84. self.flush() # Flush potentially unwritten data
  85. self.storage.close()