# HG changeset patch # User Pierre-Yves David # Date 2013-09-30 12:36:11 # Node ID a1237a4b437d6404d035c49573955cedaa57236b # Parent bbeee568a84d37eaed482d1afc9acc66c18b9682 repoview: make propertycache.setcache compatible with repoview Propertycache used standard attribute assignment. In the repoview case, this assignment was forwarded to the unfiltered repo. This result in: (1) unfiltered repo got a potentially wrong cache value, (2) repoview never reused the cached value. This patch replaces the standard attribute assignment by an assignment to `objc.__dict__` which will bypass the `repoview.__setattr__`. This will not affects other `propertycache` users and it is actually closer to the semantic we need. The interaction of `propertycache` and `repoview` are now tested in a python test file. diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -279,7 +279,8 @@ class propertycache(object): return result def cachevalue(self, obj, value): - setattr(obj, self.name, value) + # __dict__ assigment required to bypass __setattr__ (eg: repoview) + obj.__dict__[self.name] = value def pipefilter(s, cmd): '''filter string S through command CMD, returning its output''' diff --git a/tests/test-propertycache.py b/tests/test-propertycache.py new file mode 100644 --- /dev/null +++ b/tests/test-propertycache.py @@ -0,0 +1,94 @@ +"""test behavior of propertycache and unfiltered propertycache + +The repoview overlay is quite complexe. We test the behavior of +property cache of both localrepo and repoview to prevent +regression.""" + +import os, subprocess +import mercurial.localrepo +import mercurial.repoview +import mercurial.util +import mercurial.hg +import mercurial.ui as uimod + + +# create some special property cache that trace they call + +calllog = [] +@mercurial.util.propertycache +def testcachedfoobar(repo): + name = repo.filtername + if name is None: + name = '' + val = len(name) + calllog.append(val) + return val + +#plug them on repo +mercurial.localrepo.localrepository.testcachedfoobar = testcachedfoobar + + +# create an empty repo. and instanciate it. It is important to run +# those test on the real object to detect regression. +repopath = os.path.join(os.environ['TESTTMP'], 'repo') +subprocess.check_call(['hg', 'init', repopath]) +ui = uimod.ui() +repo = mercurial.hg.repository(ui, path=repopath).unfiltered() + + +print '' +print '=== property cache ===' +print '' +print 'calllog:', calllog +print 'cached value (unfiltered):', +print vars(repo).get('testcachedfoobar', 'NOCACHE') + +print '' +print '= first access on unfiltered, should do a call' +print 'access:', repo.testcachedfoobar +print 'calllog:', calllog +print 'cached value (unfiltered):', +print vars(repo).get('testcachedfoobar', 'NOCACHE') + +print '' +print '= second access on unfiltered, should not do call' +print 'access', repo.testcachedfoobar +print 'calllog:', calllog +print 'cached value (unfiltered):', +print vars(repo).get('testcachedfoobar', 'NOCACHE') + +print '' +print '= first access on "visible" view, should do a call' +visibleview = repo.filtered('visible') +print 'cached value ("visible" view):', +print vars(visibleview).get('testcachedfoobar', 'NOCACHE') +print 'access:', visibleview.testcachedfoobar +print 'calllog:', calllog +print 'cached value (unfiltered):', +print vars(repo).get('testcachedfoobar', 'NOCACHE') +print 'cached value ("visible" view):', +print vars(visibleview).get('testcachedfoobar', 'NOCACHE') + +print '' +print '= second access on "visible view", should not do call' +print 'access:', visibleview.testcachedfoobar +print 'calllog:', calllog +print 'cached value (unfiltered):', +print vars(repo).get('testcachedfoobar', 'NOCACHE') +print 'cached value ("visible" view):', +print vars(visibleview).get('testcachedfoobar', 'NOCACHE') + +print '' +print '= no effect on other view' +immutableview = repo.filtered('immutable') +print 'cached value ("immutable" view):', +print vars(immutableview).get('testcachedfoobar', 'NOCACHE') +print 'access:', immutableview.testcachedfoobar +print 'calllog:', calllog +print 'cached value (unfiltered):', +print vars(repo).get('testcachedfoobar', 'NOCACHE') +print 'cached value ("visible" view):', +print vars(visibleview).get('testcachedfoobar', 'NOCACHE') +print 'cached value ("immutable" view):', +print vars(immutableview).get('testcachedfoobar', 'NOCACHE') + diff --git a/tests/test-propertycache.py.out b/tests/test-propertycache.py.out new file mode 100644 --- /dev/null +++ b/tests/test-propertycache.py.out @@ -0,0 +1,36 @@ + +=== property cache === + +calllog: [] +cached value (unfiltered): NOCACHE + += first access on unfiltered, should do a call +access: 0 +calllog: [0] +cached value (unfiltered): 0 + += second access on unfiltered, should not do call +access 0 +calllog: [0] +cached value (unfiltered): 0 + += first access on "visible" view, should do a call +cached value ("visible" view): NOCACHE +access: 7 +calllog: [0, 7] +cached value (unfiltered): 0 +cached value ("visible" view): 7 + += second access on "visible view", should not do call +access: 7 +calllog: [0, 7] +cached value (unfiltered): 0 +cached value ("visible" view): 7 + += no effect on other view +cached value ("immutable" view): NOCACHE +access: 9 +calllog: [0, 7, 9] +cached value (unfiltered): 0 +cached value ("visible" view): 7 +cached value ("immutable" view): 9