storages.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. """
  2. Contains the :class:`base class <tinydb.storages.Storage>` for storages and
  3. implementations.
  4. """
  5. from abc import ABCMeta, abstractmethod
  6. import os
  7. from .utils import with_metaclass
  8. try:
  9. import ujson as json
  10. except ImportError:
  11. import json
  12. def touch(fname, create_dirs):
  13. if create_dirs:
  14. base_dir = os.path.dirname(fname)
  15. if not os.path.exists(base_dir):
  16. os.makedirs(base_dir)
  17. with open(fname, 'a'):
  18. os.utime(fname, None)
  19. class Storage(with_metaclass(ABCMeta, object)):
  20. """
  21. The abstract base class for all Storages.
  22. A Storage (de)serializes the current state of the database and stores it in
  23. some place (memory, file on disk, ...).
  24. """
  25. # Using ABCMeta as metaclass allows instantiating only storages that have
  26. # implemented read and write
  27. @abstractmethod
  28. def read(self):
  29. """
  30. Read the last stored state.
  31. Any kind of deserialization should go here.
  32. Return ``None`` here to indicate that the storage is empty.
  33. :rtype: dict
  34. """
  35. raise NotImplementedError('To be overridden!')
  36. @abstractmethod
  37. def write(self, data):
  38. """
  39. Write the current state of the database to the storage.
  40. Any kind of serialization should go here.
  41. :param data: The current state of the database.
  42. :type data: dict
  43. """
  44. raise NotImplementedError('To be overridden!')
  45. def close(self):
  46. """
  47. Optional: Close open file handles, etc.
  48. """
  49. pass
  50. class JSONStorage(Storage):
  51. """
  52. Store the data in a JSON file.
  53. """
  54. def __init__(self, path, create_dirs=False, **kwargs):
  55. """
  56. Create a new instance.
  57. Also creates the storage file, if it doesn't exist.
  58. :param path: Where to store the JSON data.
  59. :type path: str
  60. """
  61. super(JSONStorage, self).__init__()
  62. touch(path, create_dirs=create_dirs) # Create file if not exists
  63. self.kwargs = kwargs
  64. self._handle = open(path, 'r+')
  65. def close(self):
  66. self._handle.close()
  67. def read(self):
  68. # Get the file size
  69. self._handle.seek(0, os.SEEK_END)
  70. size = self._handle.tell()
  71. if not size:
  72. # File is empty
  73. return None
  74. else:
  75. self._handle.seek(0)
  76. return json.load(self._handle)
  77. def write(self, data):
  78. self._handle.seek(0)
  79. serialized = json.dumps(data, **self.kwargs)
  80. self._handle.write(serialized)
  81. self._handle.flush()
  82. os.fsync(self._handle.fileno())
  83. self._handle.truncate()
  84. class MemoryStorage(Storage):
  85. """
  86. Store the data as JSON in memory.
  87. """
  88. def __init__(self):
  89. """
  90. Create a new instance.
  91. """
  92. super(MemoryStorage, self).__init__()
  93. self.memory = None
  94. def read(self):
  95. return self.memory
  96. def write(self, data):
  97. self.memory = data