[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