浏览代码

list: --json for archive contents listing

Marian Beermann 8 年之前
父节点
当前提交
1f8c0929bf
共有 2 个文件被更改,包括 43 次插入7 次删除
  1. 6 2
      src/borg/archiver.py
  2. 37 5
      src/borg/helpers.py

+ 6 - 2
src/borg/archiver.py

@@ -962,10 +962,12 @@ class Archiver:
                 format = "{path}{NL}"
             else:
                 format = "{mode} {user:6} {group:6} {size:8} {isomtime} {path}{extra}{NL}"
-            formatter = ItemFormatter(archive, format)
 
+            formatter = ItemFormatter(archive, format, json=args.json)
+            write(safe_encode(formatter.begin()))
             for item in archive.iter_items(lambda item: matcher.match(item.path)):
                 write(safe_encode(formatter.format_item(item)))
+            write(safe_encode(formatter.end()))
         return self.exit_code
 
     def _list_repository(self, args, manifest, write):
@@ -2504,7 +2506,9 @@ class Archiver:
                                help="""specify format for file listing
                                 (default: "{mode} {user:6} {group:6} {size:8d} {isomtime} {path}{extra}{NL}")""")
         subparser.add_argument('--json', action='store_true',
-                               help='format output as JSON')
+                               help='format output as JSON. The form of --format is ignored, but keys used in it '
+                                    'are added to the JSON output. Some keys are always present. Note: JSON can only '
+                                    'represent text. A "bpath" key is therefore not available.')
         subparser.add_argument('location', metavar='REPOSITORY_OR_ARCHIVE', nargs='?', default='',
                                type=location_validator(),
                                help='repository/archive to list contents of')

+ 37 - 5
src/borg/helpers.py

@@ -5,6 +5,7 @@ import grp
 import hashlib
 import logging
 import io
+import json
 import os
 import os.path
 import platform
@@ -1620,8 +1621,9 @@ class ItemFormatter(BaseFormatter):
         assert not keys, str(keys)
         return "\n".join(help)
 
-    def __init__(self, archive, format):
+    def __init__(self, archive, format, *, json=False):
         self.archive = archive
+        self.json = json
         static_keys = {
             'archivename': archive.name,
             'archiveid': archive.fpr,
@@ -1646,7 +1648,34 @@ class ItemFormatter(BaseFormatter):
         for hash_function in hashlib.algorithms_guaranteed:
             self.add_key(hash_function, partial(self.hash_item, hash_function))
         self.used_call_keys = set(self.call_keys) & self.format_keys
-        self.item_data = static_keys
+        if self.json:
+            self.item_data = {}
+            self.format_item = self.format_item_json
+            self.first = True
+        else:
+            self.item_data = static_keys
+
+    def begin(self):
+        from borg.archiver import BorgJsonEncoder
+        if not self.json:
+            return ''
+        return textwrap.dedent("""
+        {{
+            "repository": {repository},
+            "files": [
+        """).strip().format(repository=BorgJsonEncoder().encode(self.archive.repository))
+
+    def end(self):
+        if not self.json:
+            return ''
+        return "]}"
+
+    def format_item_json(self, item):
+        if self.first:
+            self.first = False
+            return json.dumps(self.get_item_data(item))
+        else:
+            return ',' + json.dumps(self.get_item_data(item))
 
     def add_key(self, key, callable_with_item):
         self.call_keys[key] = callable_with_item
@@ -1673,12 +1702,15 @@ class ItemFormatter(BaseFormatter):
         item_data['uid'] = item.uid
         item_data['gid'] = item.gid
         item_data['path'] = remove_surrogates(item.path)
-        item_data['bpath'] = item.path
+        if self.json:
+            item_data['healthy'] = 'chunks_healthy' not in item
+        else:
+            item_data['bpath'] = item.path
+            item_data['extra'] = extra
+            item_data['health'] = 'broken' if 'chunks_healthy' in item else 'healthy'
         item_data['source'] = source
         item_data['linktarget'] = source
-        item_data['extra'] = extra
         item_data['flags'] = item.get('bsdflags')
-        item_data['health'] = 'broken' if 'chunks_healthy' in item else 'healthy'
         for key in self.used_call_keys:
             item_data[key] = self.call_keys[key](item)
         return item_data