diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -317,7 +317,7 @@ class dirstate(object): return copies def setbranch(self, branch): - self._branch = encoding.fromlocal(branch) + self.__class__._branch.set(self, encoding.fromlocal(branch)) f = self._opener('branch', 'w', atomictemp=True, checkambig=True) try: f.write(self._branch + '\n') diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -91,17 +91,16 @@ class _basefilecache(scmutil.filecache): def __get__(self, repo, type=None): if repo is None: return self - # inlined the fast path as the cost of function call matters + # proxy to unfiltered __dict__ since filtered repo has no entry unfi = repo.unfiltered() try: return unfi.__dict__[self.sname] except KeyError: pass return super(_basefilecache, self).__get__(unfi, type) - def __set__(self, repo, value): - return super(_basefilecache, self).__set__(repo.unfiltered(), value) - def __delete__(self, repo): - return super(_basefilecache, self).__delete__(repo.unfiltered()) + + def set(self, repo, value): + return super(_basefilecache, self).set(repo.unfiltered(), value) class repofilecache(_basefilecache): """filecache for files in .hg but outside of .hg/store""" diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -1249,16 +1249,15 @@ class filecache(object): results cached. The decorated function is called. The results are stashed away in a ``_filecache`` dict on the object whose method is decorated. - On subsequent access, the cached result is returned. - - On external property set operations, stat() calls are performed and the new - value is cached. + On subsequent access, the cached result is used as it is set to the + instance dictionary. - On property delete operations, cached data is removed. + On external property set/delete operations, the caller must update the + corresponding _filecache entry appropriately. Use __class__..set() + instead of directly setting . - When using the property API, cached data is always returned, if available: - no stat() is performed to check if the file has changed and if the function - needs to be called to reflect file changes. + When using the property API, the cached data is always used if available. + No stat() is performed to check if the file has changed. Others can muck about with the state of the ``_filecache`` dict. e.g. they can populate an entry before the property's getter is called. In this case, @@ -1291,11 +1290,8 @@ class filecache(object): # if accessed on the class, return the descriptor itself. if obj is None: return self - # do we need to check if the file changed? - try: - return obj.__dict__[self.sname] - except KeyError: - pass + + assert self.sname not in obj.__dict__ entry = obj._filecache.get(self.name) @@ -1315,7 +1311,10 @@ class filecache(object): obj.__dict__[self.sname] = entry.obj return entry.obj - def __set__(self, obj, value): + # don't implement __set__(), which would make __dict__ lookup as slow as + # function call. + + def set(self, obj, value): if self.name not in obj._filecache: # we add an entry for the missing value because X in __dict__ # implies X in _filecache @@ -1328,12 +1327,6 @@ class filecache(object): ce.obj = value # update cached copy obj.__dict__[self.sname] = value # update copy returned by obj.x - def __delete__(self, obj): - try: - del obj.__dict__[self.sname] - except KeyError: - raise AttributeError(self.sname) - def extdatasource(repo, source): """Gather a map of rev -> value dict from the specified source diff --git a/tests/test-filecache.py b/tests/test-filecache.py --- a/tests/test-filecache.py +++ b/tests/test-filecache.py @@ -177,7 +177,7 @@ def test_filecache_synced(): def setbeforeget(repo): os.remove('x') os.remove('y') - repo.cached = 'string set externally' + repo.__class__.cached.set(repo, 'string set externally') repo.invalidate() print("* neither file exists") print(repo.cached) @@ -188,7 +188,7 @@ def setbeforeget(repo): print("* file x created") print(repo.cached) - repo.cached = 'string 2 set externally' + repo.__class__.cached.set(repo, 'string 2 set externally') repo.invalidate() print("* string set externally again") print(repo.cached)