making pickled caches block no execution
[theonering] / src / gvoice / addressbook.py
index fea9ef6..4b3793b 100644 (file)
@@ -1,10 +1,20 @@
 #!/usr/bin/python
 
+from __future__ import with_statement
+
 
 import logging
 
+try:
+       import cPickle
+       pickle = cPickle
+except ImportError:
+       import pickle
+
+import constants
 import util.coroutines as coroutines
 import util.misc as misc_utils
+import util.go_utils as gobject_utils
 
 
 _moduleLogger = logging.getLogger(__name__)
@@ -15,6 +25,8 @@ class Addressbook(object):
        _RESPONSE_GOOD = 0
        _RESPONSE_BLOCKED = 3
 
+       OLDEST_COMPATIBLE_FORMAT_VERSION = misc_utils.parse_version("0.8.0")
+
        def __init__(self, backend, asyncPool):
                self._backend = backend
                self._numbers = {}
@@ -22,18 +34,63 @@ class Addressbook(object):
 
                self.updateSignalHandler = coroutines.CoTee()
 
+       def load(self, path):
+               _moduleLogger.debug("Loading cache")
+               assert not self._numbers
+               try:
+                       with open(path, "rb") as f:
+                               fileVersion, fileBuild, contacts = pickle.load(f)
+               except (pickle.PickleError, IOError, EOFError, ValueError, Exception):
+                       _moduleLogger.exception("While loading")
+                       return
+
+               if contacts and misc_utils.compare_versions(
+                       self.OLDEST_COMPATIBLE_FORMAT_VERSION,
+                       misc_utils.parse_version(fileVersion),
+               ) <= 0:
+                       _moduleLogger.info("Loaded cache")
+                       self._numbers = contacts
+                       self._loadedFromCache = True
+               else:
+                       _moduleLogger.debug(
+                               "Skipping cache due to version mismatch (%s-%s)" % (
+                                       fileVersion, fileBuild
+                               )
+                       )
+
+       def save(self, path):
+               _moduleLogger.info("Saving cache")
+               if not self._numbers:
+                       _moduleLogger.info("Odd, no conversations to cache.  Did we never load the cache?")
+                       return
+
+               try:
+                       dataToDump = (constants.__version__, constants.__build__, self._numbers)
+                       with open(path, "wb") as f:
+                               pickle.dump(dataToDump, f, pickle.HIGHEST_PROTOCOL)
+               except (pickle.PickleError, IOError):
+                       _moduleLogger.exception("While saving for %s" % self._name)
+               _moduleLogger.info("Cache saved")
+
        def update(self, force=False):
                if not force and self._numbers:
                        return
-               self._asyncPool.add_task(
-                       self._backend.get_contacts,
-                       (),
-                       {},
-                       self._on_get_contacts,
-                       self._on_get_contacts_failed,
-               )
 
-       def _on_get_contacts(self, contacts):
+               le = gobject_utils.AsyncLinearExecution(self._asyncPool, self._update)
+               le.start()
+
+       @misc_utils.log_exception(_moduleLogger)
+       def _update(self):
+               try:
+                       contacts = yield (
+                               self._backend.get_contacts,
+                               (),
+                               {},
+                       )
+               except Exception:
+                       _moduleLogger.exception("While updating the addressbook")
+                       return
+
                oldContacts = self._numbers
                oldContactNumbers = set(self.get_numbers())
 
@@ -52,10 +109,6 @@ class Addressbook(object):
                        message = self, addedContacts, removedContacts, changedContacts
                        self.updateSignalHandler.stage.send(message)
 
-       @misc_utils.log_exception(_moduleLogger)
-       def _on_get_contacts_failed(self, error):
-               _moduleLogger.error(error)
-
        def get_numbers(self):
                return self._numbers.iterkeys()
 
@@ -93,3 +146,16 @@ class Addressbook(object):
                                for (number, phoneType) in contactNumbers
                        )
                return numbers
+
+
+def print_addressbook(path):
+       import pprint
+
+       try:
+               with open(path, "rb") as f:
+                       fileVersion, fileBuild, contacts = pickle.load(f)
+       except (pickle.PickleError, IOError, EOFError, ValueError):
+               _moduleLogger.exception("")
+       else:
+               pprint.pprint((fileVersion, fileBuild))
+               pprint.pprint(contacts)