PreservedLibsRegistry: add JSON read/write
authorZac Medico <zmedico@gentoo.org>
Sat, 18 Feb 2012 02:01:27 +0000 (18:01 -0800)
committerZac Medico <zmedico@gentoo.org>
Sat, 18 Feb 2012 03:11:18 +0000 (19:11 -0800)
Support serialization as JSON instead of pickle, so that
/var/lib/portage/preserved_libs_registry is human readable/writable,
for those rare cases where it may be useful. Currently, pickle is still
used for writes. The plan is to migrate to JSON after JSON read has
been supported for some time.

pym/portage/util/_dyn_libs/PreservedLibsRegistry.py

index 510c390af9a3f56aa9309adfd8d868be1c6be781..405a23a88bce9e47d3d2925f097d69c187aee732 100644 (file)
@@ -1,7 +1,8 @@
-# Copyright 1998-2011 Gentoo Foundation
+# Copyright 1998-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
+import json
 import logging
 import sys
 
@@ -27,6 +28,19 @@ if sys.hexversion >= 0x3000000:
 
 class PreservedLibsRegistry(object):
        """ This class handles the tracking of preserved library objects """
+
+       # Enable this after JSON read has been supported for some time.
+       _json_write = False
+
+       _json_write_opts = {
+               "ensure_ascii": False,
+               "indent": "\t",
+               "sort_keys": True
+       }
+       if sys.hexversion < 0x3020000:
+               # indent only supports int number of spaces
+               _json_write_opts["indent"] = 4
+
        def __init__(self, root, filename):
                """ 
                        @param root: root used to check existence of paths in pruneNonExisting
@@ -56,17 +70,11 @@ class PreservedLibsRegistry(object):
                """ Reload the registry data from file """
                self._data = None
                f = None
+               content = None
                try:
                        f = open(_unicode_encode(self._filename,
                                        encoding=_encodings['fs'], errors='strict'), 'rb')
-                       if os.fstat(f.fileno()).st_size == 0:
-                               # ignore empty lock file
-                               pass
-                       else:
-                               self._data = pickle.load(f)
-               except (AttributeError, EOFError, ValueError, pickle.UnpicklingError) as e:
-                       writemsg_level(_("!!! Error loading '%s': %s\n") % \
-                               (self._filename, e), level=logging.ERROR, noiselevel=-1)
+                       content = f.read()
                except EnvironmentError as e:
                        if not hasattr(e, 'errno'):
                                raise
@@ -79,8 +87,33 @@ class PreservedLibsRegistry(object):
                finally:
                        if f is not None:
                                f.close()
+
+               # content is empty if it's an empty lock file
+               if content:
+                       try:
+                               self._data = pickle.loads(content)
+                       except SystemExit:
+                               raise
+                       except Exception as e:
+                               try:
+                                       self._data = json.loads(_unicode_decode(content,
+                                               encoding=_encodings['repo.content'], errors='strict'))
+                               except SystemExit:
+                                       raise
+                               except Exception:
+                                       writemsg_level(_("!!! Error loading '%s': %s\n") %
+                                               (self._filename, e), level=logging.ERROR,
+                                               noiselevel=-1)
+
                if self._data is None:
                        self._data = {}
+               else:
+                       for k, v in self._data.items():
+                               if isinstance(v, (list, tuple)) and len(v) == 3 and \
+                                       isinstance(v[2], set):
+                                       # convert set to list, for write with JSONEncoder
+                                       self._data[k] = (v[0], v[1], list(v[2]))
+
                self._data_orig = self._data.copy()
                self.pruneNonExisting()
 
@@ -97,7 +130,12 @@ class PreservedLibsRegistry(object):
                        return
                try:
                        f = atomic_ofstream(self._filename, 'wb')
-                       pickle.dump(self._data, f, protocol=2)
+                       if self._json_write:
+                               f.write(_unicode_encode(
+                                       json.dumps(self._data, **self._json_write_opts),
+                                       encoding=_encodings['repo.content'], errors='strict'))
+                       else:
+                               pickle.dump(self._data, f, protocol=2)
                        f.close()
                except EnvironmentError as e:
                        if e.errno != PermissionDenied.errno:
@@ -138,6 +176,9 @@ class PreservedLibsRegistry(object):
                                self._normalize_counter(self._data[cps][1]) == counter:
                        del self._data[cps]
                elif len(paths) > 0:
+                       if isinstance(paths, set):
+                               # convert set to list, for write with JSONEncoder
+                               paths = list(paths)
                        self._data[cps] = (cpv, counter, paths)
 
        def unregister(self, cpv, slot, counter):