[SCM] morituri/master: * morituri/common/cache.py (added): * morituri/test/cache (added): * morituri/test/cache/result (added): * morituri/test/cache/result/fe105a11.pickle (added): * morituri/test/test_common_cache.py (added): * morituri/common/Makefile.am: * morituri/common/program.py: * morituri/test/Makefile.am: Extract ResultCache object into separate file.

js at users.alioth.debian.org js at users.alioth.debian.org
Sun Oct 19 20:09:47 UTC 2014


The following commit has been merged in the master branch:
commit c634dd0e9290005d3896506af490b4bfc9a12e83
Author: Thomas Vander Stichele <thomas (at) apestaart (dot) org>
Date:   Sun Dec 2 16:38:03 2012 +0000

    	* morituri/common/cache.py (added):
    	* morituri/test/cache (added):
    	* morituri/test/cache/result (added):
    	* morituri/test/cache/result/fe105a11.pickle (added):
    	* morituri/test/test_common_cache.py (added):
    	* morituri/common/Makefile.am:
    	* morituri/common/program.py:
    	* morituri/test/Makefile.am:
    	  Extract ResultCache object into separate file.

diff --git a/ChangeLog b/ChangeLog
index 5d60e1e..277b19d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2012-12-02  Thomas Vander Stichele  <thomas at apestaart dot org>
 
+	* morituri/common/cache.py (added):
+	* morituri/test/cache (added):
+	* morituri/test/cache/result (added):
+	* morituri/test/cache/result/fe105a11.pickle (added):
+	* morituri/test/test_common_cache.py (added):
+	* morituri/common/Makefile.am:
+	* morituri/common/program.py:
+	* morituri/test/Makefile.am:
+	  Extract ResultCache object into separate file.
+
+2012-12-02  Thomas Vander Stichele  <thomas at apestaart dot org>
+
 	* morituri/rip/drive.py:
 	  rip drive list now shows configured read offset if applicable.
 
diff --git a/morituri/common/Makefile.am b/morituri/common/Makefile.am
index b3304f6..3d64742 100644
--- a/morituri/common/Makefile.am
+++ b/morituri/common/Makefile.am
@@ -6,6 +6,7 @@ morituri_PYTHON = \
 	__init__.py \
 	accurip.py \
 	checksum.py \
+	cache.py \
 	common.py \
 	config.py \
 	drive.py \
diff --git a/morituri/common/cache.py b/morituri/common/cache.py
new file mode 100644
index 0000000..3e372f4
--- /dev/null
+++ b/morituri/common/cache.py
@@ -0,0 +1,177 @@
+# -*- Mode: Python; test-case-name: morituri.test.test_common_cache -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+# Morituri - for those about to RIP
+
+# Copyright (C) 2009 Thomas Vander Stichele
+
+# This file is part of morituri.
+#
+# morituri is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# morituri is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with morituri.  If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import os.path
+import tempfile
+import shutil
+
+from morituri.result import result
+from morituri.extern.log import log
+
+
+class Persister(object):
+    """
+    I wrap an optional pickle to persist an object to disk.
+
+    Instantiate me with a path to automatically unpickle the object.
+    Call persist to store the object to disk; it will get stored if it
+    changed from the on-disk object.
+
+    @ivar object: the persistent object
+    """
+
+    def __init__(self, path=None, default=None):
+        """
+        If path is not given, the object will not be persisted.
+        This allows code to transparently deal with both persisted and
+        non-persisted objects, since the persist method will just end up
+        doing nothing.
+        """
+        self._path = path
+        self.object = None
+
+        self._unpickle(default)
+
+    def persist(self, obj=None):
+        """
+        Persist the given object, if we have a persistence path and the
+        object changed.
+
+        If object is not given, re-persist our object, always.
+        If object is given, only persist if it was changed.
+        """
+        # don't pickle if it's already ok
+        if obj and obj == self.object:
+            return
+
+        # store the object on ourselves if not None
+        if obj is not None:
+            self.object = obj
+
+        # don't pickle if there is no path
+        if not self._path:
+            return
+
+        # default to pickling our object again
+        if obj is None:
+            obj = self.object
+
+        # pickle
+        self.object = obj
+        (fd, path) = tempfile.mkstemp(suffix='.morituri.pickle')
+        handle = os.fdopen(fd, 'wb')
+        import pickle
+        pickle.dump(obj, handle, 2)
+        handle.close()
+        # do an atomic move
+        shutil.move(path, self._path)
+
+    def _unpickle(self, default=None):
+        self.object = default
+
+        if not self._path:
+            return None
+
+        if not os.path.exists(self._path):
+            return None
+
+        handle = open(self._path)
+        import pickle
+
+        try:
+            self.object = pickle.load(handle)
+        except:
+            # can fail for various reasons; in that case, pretend we didn't
+            # load it
+            pass
+
+    def delete(self):
+        self.object = None
+        os.unlink(self._path)
+
+
+class PersistedCache(object):
+    """
+    I wrap a directory of persisted objects.
+    """
+
+    path = None
+
+    def __init__(self, path):
+        self.path = path
+        try:
+            os.makedirs(self.path)
+        except OSError, e:
+            if e.errno != 17: # FIXME
+                raise
+
+    def _getPath(self, key):
+        return os.path.join(self.path, '%s.pickle' % key)
+
+    def get(self, key):
+        """
+        Returns the persister for the given key.
+        """
+        persister = Persister(self._getPath(key))
+        if persister.object:
+            if hasattr(persister.object, 'instanceVersion'):
+                o = persister.object
+                if o.instanceVersion < o.__class__.classVersion:
+                    persister.delete()
+
+        return persister
+
+
+class ResultCache(log.Loggable):
+
+    def __init__(self, path=None):
+        if not path:
+            path = self._getResultCachePath()
+
+        self._path = path
+        self._pcache = PersistedCache(self._path)
+
+    def _getResultCachePath(self):
+        path = os.path.join(os.path.expanduser('~'), '.morituri', 'cache',
+            'result')
+        return path
+
+    def getRipResult(self, cddbdiscid):
+        """
+        Retrieve the persistable RipResult either from our cache (from a
+        previous, possibly aborted rip), or return a new one.
+
+        @rtype: L{Persistable} for L{result.RipResult}
+        """
+        presult = self._pcache.get(cddbdiscid)
+
+        if not presult.object:
+            self.debug('result for cddbdiscid %r not in cache, creating',
+                cddbdiscid)
+            presult.object = result.RipResult()
+            presult.persist(self.result)
+        else:
+            self.debug('result for cddbdiscid %r found in cache, reusing',
+                cddbdiscid)
+
+        return presult
diff --git a/morituri/common/program.py b/morituri/common/program.py
index 1c970f0..afdb5c9 100644
--- a/morituri/common/program.py
+++ b/morituri/common/program.py
@@ -27,8 +27,7 @@ Common functionality and class for all programs using morituri.
 import os
 import time
 
-from morituri.common import common, log, musicbrainzngs
-from morituri.result import result
+from morituri.common import common, log, musicbrainzngs, cache
 from morituri.program import cdrdao, cdparanoia
 from morituri.image import image
 
@@ -59,16 +58,13 @@ class Program(log.Loggable):
         @param record: whether to record results of API calls for playback.
         """
         self._record = record
+        self._cache = cache.ResultCache()
 
     def _getTableCachePath(self):
         path = os.path.join(os.path.expanduser('~'), '.morituri', 'cache',
             'table')
         return path
 
-    def _getResultCachePath(self):
-        path = os.path.join(os.path.expanduser('~'), '.morituri', 'cache',
-            'result')
-        return path
 
     def loadDevice(self, device):
         """
@@ -129,22 +125,8 @@ class Program(log.Loggable):
         """
         assert self.result is None
 
-        path = self._getResultCachePath()
-
-        pcache = common.PersistedCache(path)
-        presult = pcache.get(cddbdiscid)
-
-        if not presult.object:
-            self.debug('result for cddbdiscid %r not in cache, creating',
-                cddbdiscid)
-            presult.object = result.RipResult()
-            presult.persist(self.result)
-        else:
-            self.debug('result for cddbdiscid %r found in cache, reusing',
-                cddbdiscid)
-
-        self.result = presult.object
-        self._presult = presult
+        self._presult = self._cache.getRipResult(cddbdiscid)
+        self.result = self._presult.object
 
         return self.result
 
diff --git a/morituri/test/Makefile.am b/morituri/test/Makefile.am
index f620a36..deb8b9a 100644
--- a/morituri/test/Makefile.am
+++ b/morituri/test/Makefile.am
@@ -4,6 +4,7 @@ EXTRA_DIST = \
 	__init__.py \
 	common.py \
 	test_common_accurip.py \
+	test_common_cache.py \
 	test_common_checksum.py \
 	test_common_common.py \
 	test_common_config.py \
@@ -45,7 +46,8 @@ EXTRA_DIST = \
 	cdparanoia.progress.error \
 	cdrdao.readtoc.progress \
 	silentalarm.result.pickle \
-	track.flac
+	track.flac \
+	cache/result/fe105a11.pickle
 
 
 # re-generation of test files when needed
@@ -57,4 +59,3 @@ regenerate: track.flac
 
 track.flac:
 	gst-launch audiotestsrc num-buffers=10 samplesperbuffer=588 ! audioconvert ! audio/x-raw-int,channels=2,width=16,height=16,rate=44100 ! flacenc ! filesink location=track.flac
-
diff --git a/morituri/test/cache/result/fe105a11.pickle b/morituri/test/cache/result/fe105a11.pickle
new file mode 100644
index 0000000..97c91cc
Binary files /dev/null and b/morituri/test/cache/result/fe105a11.pickle differ
diff --git a/morituri/test/test_common_cache.py b/morituri/test/test_common_cache.py
new file mode 100644
index 0000000..6dfe87d
--- /dev/null
+++ b/morituri/test/test_common_cache.py
@@ -0,0 +1,19 @@
+# -*- Mode: Python; test-case-name: morituri.test.test_common_cache -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+import os
+
+from morituri.common import cache
+
+from morituri.test import common as tcommon
+
+
+class ResultCacheTestCase(tcommon.TestCase):
+
+    def setUp(self):
+        self.cache = cache.ResultCache(
+            os.path.join(os.path.dirname(__file__), 'cache', 'result'))
+
+    def testGet(self):
+        result = self.cache.getRipResult('fe105a11')
+        self.assertEquals(result.object.title, "The Writing's on the Wall")

-- 
morituri packaging



More information about the pkg-multimedia-commits mailing list