mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 23:07:18 +03:00
scmutil.filecache: support watching over multiple files
This commit is contained in:
parent
fceaf0623e
commit
25418f3155
@ -786,23 +786,26 @@ class filecacheentry(object):
|
||||
entry.refresh()
|
||||
|
||||
class filecache(object):
|
||||
'''A property like decorator that tracks a file under .hg/ for updates.
|
||||
'''A property like decorator that tracks files under .hg/ for updates.
|
||||
|
||||
Records stat info when called in _filecache.
|
||||
|
||||
On subsequent calls, compares old stat info with new info, and recreates
|
||||
the object when needed, updating the new stat info in _filecache.
|
||||
On subsequent calls, compares old stat info with new info, and recreates the
|
||||
object when any of the files changes, updating the new stat info in
|
||||
_filecache.
|
||||
|
||||
Mercurial either atomic renames or appends for files under .hg,
|
||||
so to ensure the cache is reliable we need the filesystem to be able
|
||||
to tell us if a file has been replaced. If it can't, we fallback to
|
||||
recreating the object on every call (essentially the same behaviour as
|
||||
propertycache).'''
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
propertycache).
|
||||
|
||||
'''
|
||||
def __init__(self, *paths):
|
||||
self.paths = paths
|
||||
|
||||
def join(self, obj, fname):
|
||||
"""Used to compute the runtime path of the cached file.
|
||||
"""Used to compute the runtime path of a cached file.
|
||||
|
||||
Users should subclass filecache and provide their own version of this
|
||||
function to call the appropriate join function on 'obj' (an instance
|
||||
@ -827,11 +830,11 @@ class filecache(object):
|
||||
if entry.changed():
|
||||
entry.obj = self.func(obj)
|
||||
else:
|
||||
path = self.join(obj, self.path)
|
||||
paths = [self.join(obj, path) for path in self.paths]
|
||||
|
||||
# We stat -before- creating the object so our cache doesn't lie if
|
||||
# a writer modified between the time we read and stat
|
||||
entry = filecachesubentry(path, True)
|
||||
entry = filecacheentry(paths, True)
|
||||
entry.obj = self.func(obj)
|
||||
|
||||
obj._filecache[self.name] = entry
|
||||
@ -843,7 +846,8 @@ class filecache(object):
|
||||
if self.name not in obj._filecache:
|
||||
# we add an entry for the missing value because X in __dict__
|
||||
# implies X in _filecache
|
||||
ce = filecachesubentry(self.join(obj, self.path), False)
|
||||
paths = [self.join(obj, path) for path in self.paths]
|
||||
ce = filecacheentry(paths, False)
|
||||
obj._filecache[self.name] = ce
|
||||
else:
|
||||
ce = obj._filecache[self.name]
|
||||
|
@ -18,7 +18,7 @@ class fakerepo(object):
|
||||
def sjoin(self, p):
|
||||
return p
|
||||
|
||||
@filecache('x')
|
||||
@filecache('x', 'y')
|
||||
def cached(self):
|
||||
print 'creating'
|
||||
return 'string from function'
|
||||
@ -31,12 +31,12 @@ class fakerepo(object):
|
||||
pass
|
||||
|
||||
def basic(repo):
|
||||
print "* file doesn't exist"
|
||||
print "* neither file exists"
|
||||
# calls function
|
||||
repo.cached
|
||||
|
||||
repo.invalidate()
|
||||
print "* file still doesn't exist"
|
||||
print "* neither file still exists"
|
||||
# uses cache
|
||||
repo.cached
|
||||
|
||||
@ -57,7 +57,7 @@ def basic(repo):
|
||||
repo.cached
|
||||
|
||||
repo.invalidate()
|
||||
print "* nothing changed with file x"
|
||||
print "* nothing changed with either file"
|
||||
# stats file again, reuses object
|
||||
repo.cached
|
||||
|
||||
@ -72,6 +72,41 @@ def basic(repo):
|
||||
print "* file x changed inode"
|
||||
repo.cached
|
||||
|
||||
# create empty file y
|
||||
f = open('y', 'w')
|
||||
f.close()
|
||||
repo.invalidate()
|
||||
print "* empty file y created"
|
||||
# should recreate the object
|
||||
repo.cached
|
||||
|
||||
f = open('y', 'w')
|
||||
f.write('A')
|
||||
f.close()
|
||||
repo.invalidate()
|
||||
print "* file y changed size"
|
||||
# should recreate the object
|
||||
repo.cached
|
||||
|
||||
f = scmutil.opener('.')('y', 'w', atomictemp=True)
|
||||
f.write('B')
|
||||
f.close()
|
||||
|
||||
repo.invalidate()
|
||||
print "* file y changed inode"
|
||||
repo.cached
|
||||
|
||||
f = scmutil.opener('.')('x', 'w', atomictemp=True)
|
||||
f.write('c')
|
||||
f.close()
|
||||
f = scmutil.opener('.')('y', 'w', atomictemp=True)
|
||||
f.write('C')
|
||||
f.close()
|
||||
|
||||
repo.invalidate()
|
||||
print "* both files changed inode"
|
||||
repo.cached
|
||||
|
||||
def fakeuncacheable():
|
||||
def wrapcacheable(orig, *args, **kwargs):
|
||||
return False
|
||||
@ -83,10 +118,11 @@ def fakeuncacheable():
|
||||
origcacheable = extensions.wrapfunction(util.cachestat, 'cacheable',
|
||||
wrapcacheable)
|
||||
|
||||
try:
|
||||
os.remove('x')
|
||||
except OSError:
|
||||
pass
|
||||
for fn in ['x', 'y']:
|
||||
try:
|
||||
os.remove(fn)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
basic(fakerepo())
|
||||
|
||||
@ -110,9 +146,10 @@ def test_filecache_synced():
|
||||
|
||||
def setbeforeget(repo):
|
||||
os.remove('x')
|
||||
os.remove('y')
|
||||
repo.cached = 'string set externally'
|
||||
repo.invalidate()
|
||||
print "* file x doesn't exist"
|
||||
print "* neither file exists"
|
||||
print repo.cached
|
||||
repo.invalidate()
|
||||
f = open('x', 'w')
|
||||
@ -121,6 +158,18 @@ def setbeforeget(repo):
|
||||
print "* file x created"
|
||||
print repo.cached
|
||||
|
||||
repo.cached = 'string 2 set externally'
|
||||
repo.invalidate()
|
||||
print "* string set externally again"
|
||||
print repo.cached
|
||||
|
||||
repo.invalidate()
|
||||
f = open('y', 'w')
|
||||
f.write('b')
|
||||
f.close()
|
||||
print "* file y created"
|
||||
print repo.cached
|
||||
|
||||
print 'basic:'
|
||||
print
|
||||
basic(fakerepo())
|
||||
|
@ -1,30 +1,46 @@
|
||||
basic:
|
||||
|
||||
* file doesn't exist
|
||||
* neither file exists
|
||||
creating
|
||||
* file still doesn't exist
|
||||
* neither file still exists
|
||||
* empty file x created
|
||||
creating
|
||||
* file x changed size
|
||||
creating
|
||||
* nothing changed with file x
|
||||
* nothing changed with either file
|
||||
* file x changed inode
|
||||
creating
|
||||
* empty file y created
|
||||
creating
|
||||
* file y changed size
|
||||
creating
|
||||
* file y changed inode
|
||||
creating
|
||||
* both files changed inode
|
||||
creating
|
||||
|
||||
fakeuncacheable:
|
||||
|
||||
* file doesn't exist
|
||||
* neither file exists
|
||||
creating
|
||||
* file still doesn't exist
|
||||
* neither file still exists
|
||||
creating
|
||||
* empty file x created
|
||||
creating
|
||||
* file x changed size
|
||||
creating
|
||||
* nothing changed with file x
|
||||
* nothing changed with either file
|
||||
creating
|
||||
* file x changed inode
|
||||
creating
|
||||
* empty file y created
|
||||
creating
|
||||
* file y changed size
|
||||
creating
|
||||
* file y changed inode
|
||||
creating
|
||||
* both files changed inode
|
||||
creating
|
||||
repository tip rolled back to revision -1 (undo commit)
|
||||
working directory now based on revision -1
|
||||
repository tip rolled back to revision -1 (undo commit)
|
||||
@ -32,8 +48,13 @@ working directory now based on revision -1
|
||||
|
||||
setbeforeget:
|
||||
|
||||
* file x doesn't exist
|
||||
* neither file exists
|
||||
string set externally
|
||||
* file x created
|
||||
creating
|
||||
string from function
|
||||
* string set externally again
|
||||
string 2 set externally
|
||||
* file y created
|
||||
creating
|
||||
string from function
|
||||
|
Loading…
Reference in New Issue
Block a user