changeset 18013:98c867ac1330

clfilter: add a propertycache that must be unfiltered Some of the localrepo property caches must be computed unfiltered and stored globally. Some others must see the filtered version and store data relative to the current filtering. This changeset introduces two classes `unfilteredpropertycache` and `filteredpropertycache` for this purpose. A new function `hasunfilteredcache` is introduced for unambiguous checking for cached values on unfiltered repos. A few tweaks are made to the property cache class to allow overriding the way the computed value is stored on the object. Some logic relative to _tagcaches is cleaned up in the process.
author Pierre-Yves David <pierre-yves.david@logilab.fr>
date Mon, 08 Oct 2012 20:02:20 +0200
parents 848c428bb5ee
children a39fe76c4c65
files mercurial/localrepo.py mercurial/util.py
diffstat 2 files changed, 30 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/localrepo.py	Mon Oct 08 18:11:56 2012 +0200
+++ b/mercurial/localrepo.py	Mon Oct 08 20:02:20 2012 +0200
@@ -23,6 +23,23 @@
     def join(self, obj, fname):
         return obj.sjoin(fname)
 
+class unfilteredpropertycache(propertycache):
+    """propertycache that apply to unfiltered repo only"""
+
+    def __get__(self, repo, type=None):
+        return super(unfilteredpropertycache, self).__get__(repo.unfiltered())
+
+class filteredpropertycache(propertycache):
+    """propertycache that must take filtering in account"""
+
+    def cachevalue(self, obj, value):
+        object.__setattr__(obj, self.name, value)
+
+
+def hasunfilteredcache(repo, name):
+    """check if an repo and a unfilteredproperty cached value for <name>"""
+    return name in vars(repo.unfiltered())
+
 def unfilteredmeth(orig):
     """decorate method that always need to be run on unfiltered version"""
     def wrapper(repo, *args, **kwargs):
@@ -304,7 +321,7 @@
             self.ui.warn(msg % len(list(store)))
         return store
 
-    @propertycache
+    @unfilteredpropertycache
     def hiddenrevs(self):
         """hiddenrevs: revs that should be hidden by command and tools
 
@@ -492,7 +509,7 @@
         self.tags() # instantiate the cache
         self._tag(names, node, message, local, user, date)
 
-    @propertycache
+    @filteredpropertycache
     def _tagscache(self):
         '''Returns a tagscache object that contains various tags related
         caches.'''
@@ -879,11 +896,11 @@
 
         return data
 
-    @propertycache
+    @unfilteredpropertycache
     def _encodefilterpats(self):
         return self._loadfilter('encode')
 
-    @propertycache
+    @unfilteredpropertycache
     def _decodefilterpats(self):
         return self._loadfilter('decode')
 
@@ -1049,13 +1066,10 @@
         return 0
 
     def invalidatecaches(self):
-        def delcache(name):
-            try:
-                delattr(self, name)
-            except AttributeError:
-                pass
 
-        delcache('_tagscache')
+        if '_tagscache' in vars(self):
+            # can't use delattr on proxy
+            del self.__dict__['_tagscache']
 
         self.unfiltered()._branchcache = None # in UTF-8
         self.unfiltered()._branchcachetip = None
@@ -1070,7 +1084,7 @@
         rereads the dirstate. Use dirstate.invalidate() if you want to
         explicitly read the dirstate again (i.e. restoring it to a previous
         known good state).'''
-        if 'dirstate' in self.__dict__:
+        if hasunfilteredcache(self, 'dirstate'):
             for k in self.dirstate._filecache:
                 try:
                     delattr(self.dirstate, k)
@@ -1127,7 +1141,7 @@
 
         def unlock():
             self.store.write()
-            if '_phasecache' in vars(self):
+            if hasunfilteredcache(self, '_phasecache'):
                 self._phasecache.write()
             for k, ce in self._filecache.items():
                 if k == 'dirstate':
--- a/mercurial/util.py	Mon Oct 08 18:11:56 2012 +0200
+++ b/mercurial/util.py	Mon Oct 08 20:02:20 2012 +0200
@@ -244,9 +244,12 @@
         self.name = func.__name__
     def __get__(self, obj, type=None):
         result = self.func(obj)
-        setattr(obj, self.name, result)
+        self.cachevalue(obj, result)
         return result
 
+    def cachevalue(self, obj, value):
+        setattr(obj, self.name, value)
+
 def pipefilter(s, cmd):
     '''filter string S through command CMD, returning its output'''
     p = subprocess.Popen(cmd, shell=True, close_fds=closefds,