فهرست منبع

add and use ManifestItem

Thomas Waldmann 8 سال پیش
والد
کامیت
b6d0eb99a5
2فایلهای تغییر یافته به همراه39 افزوده شده و 15 حذف شده
  1. 16 15
      src/borg/helpers.py
  2. 23 0
      src/borg/item.py

+ 16 - 15
src/borg/helpers.py

@@ -114,6 +114,7 @@ class Manifest:
 
     @classmethod
     def load(cls, repository, key=None):
+        from .item import ManifestItem
         from .key import key_factory
         from .repository import Repository
         try:
@@ -125,27 +126,27 @@ class Manifest:
         manifest = cls(key, repository)
         _, data = key.decrypt(None, cdata)
         manifest.id = key.id_hash(data)
-        m = msgpack.unpackb(data)
-        if not m.get(b'version') == 1:
+        m = ManifestItem(internal_dict=msgpack.unpackb(data))
+        if m.get('version') != 1:
             raise ValueError('Invalid manifest version')
-        manifest.archives = dict((k.decode('utf-8'), v) for k, v in m[b'archives'].items())
-        manifest.timestamp = m.get(b'timestamp')
-        if manifest.timestamp:
-            manifest.timestamp = manifest.timestamp.decode('ascii')
-        manifest.config = m[b'config']
+        manifest.archives = {safe_decode(k): v for k, v in m.archives.items()}
+        manifest.timestamp = m.get('timestamp')
+        manifest.config = m.config
         # valid item keys are whatever is known in the repo or every key we know
-        manifest.item_keys = ITEM_KEYS | frozenset(key.decode() for key in m.get(b'item_keys', []))
+        manifest.item_keys = ITEM_KEYS | frozenset(key.decode() for key in m.get('item_keys', []))
         return manifest, key
 
     def write(self):
+        from .item import ManifestItem
         self.timestamp = datetime.utcnow().isoformat()
-        data = msgpack.packb(StableDict({
-            'version': 1,
-            'archives': self.archives,
-            'timestamp': self.timestamp,
-            'config': self.config,
-            'item_keys': tuple(self.item_keys),
-        }))
+        manifest = ManifestItem(
+            version=1,
+            archives=self.archives,
+            timestamp=self.timestamp,
+            config=self.config,
+            item_keys=tuple(self.item_keys),
+        )
+        data = msgpack.packb(manifest.as_dict())
         self.id = self.key.id_hash(data)
         self.repository.put(self.MANIFEST_ID, self.key.encrypt(Chunk(data)))
 

+ 23 - 0
src/borg/item.py

@@ -239,3 +239,26 @@ class ArchiveItem(PropDict):
     recreate_cmdline = PropDict._make_property('recreate_cmdline', list)  # list of s-e-str
     recreate_args = PropDict._make_property('recreate_args', list)  # list of s-e-str
     recreate_partial_chunks = PropDict._make_property('recreate_partial_chunks', list)  # list of tuples
+
+
+class ManifestItem(PropDict):
+    """
+    ManifestItem abstraction that deals with validation and the low-level details internally:
+
+    A ManifestItem is created either from msgpack unpacker output, from another dict, from kwargs or
+    built step-by-step by setting attributes.
+
+    msgpack gives us a dict with bytes-typed keys, just give it to ManifestItem(d) and use manifest.xxx later.
+
+    If a ManifestItem shall be serialized, give as_dict() method output to msgpack packer.
+    """
+
+    VALID_KEYS = {'version', 'archives', 'timestamp', 'config', 'item_keys', }  # str-typed keys
+
+    __slots__ = ("_dict", )  # avoid setting attributes not supported by properties
+
+    version = PropDict._make_property('version', int)
+    archives = PropDict._make_property('archives', dict)  # name -> dict
+    timestamp = PropDict._make_property('time', str, 'surrogate-escaped str', encode=safe_encode, decode=safe_decode)
+    config = PropDict._make_property('config', dict)
+    item_keys = PropDict._make_property('item_keys', tuple)