##// END OF EJS Templates
util: make atomictempfile avoid ambiguity of file stat if needed...
FUJIWARA Katsunori -
r29201:a109bf7e default
parent child Browse files
Show More
@@ -1453,11 +1453,12 b' class atomictempfile(object):'
1453 visible. If the object is destroyed without being closed, all your
1453 visible. If the object is destroyed without being closed, all your
1454 writes are discarded.
1454 writes are discarded.
1455 '''
1455 '''
1456 def __init__(self, name, mode='w+b', createmode=None):
1456 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1457 self.__name = name # permanent name
1457 self.__name = name # permanent name
1458 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1458 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1459 createmode=createmode)
1459 createmode=createmode)
1460 self._fp = posixfile(self._tempname, mode)
1460 self._fp = posixfile(self._tempname, mode)
1461 self._checkambig = checkambig
1461
1462
1462 # delegated methods
1463 # delegated methods
1463 self.write = self._fp.write
1464 self.write = self._fp.write
@@ -1468,7 +1469,17 b' class atomictempfile(object):'
1468 def close(self):
1469 def close(self):
1469 if not self._fp.closed:
1470 if not self._fp.closed:
1470 self._fp.close()
1471 self._fp.close()
1471 rename(self._tempname, localpath(self.__name))
1472 filename = localpath(self.__name)
1473 oldstat = self._checkambig and filestat(filename)
1474 if oldstat and oldstat.stat:
1475 rename(self._tempname, filename)
1476 newstat = filestat(filename)
1477 if newstat.isambig(oldstat):
1478 # stat of changed file is ambiguous to original one
1479 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1480 os.utime(filename, (advanced, advanced))
1481 else:
1482 rename(self._tempname, filename)
1472
1483
1473 def discard(self):
1484 def discard(self):
1474 if not self._fp.closed:
1485 if not self._fp.closed:
@@ -42,6 +42,46 b' class testatomictempfile(unittest.TestCa'
42 def test3_oops(self):
42 def test3_oops(self):
43 self.assertRaises(TypeError, atomictempfile)
43 self.assertRaises(TypeError, atomictempfile)
44
44
45 # checkambig=True avoids ambiguity of timestamp
46 def test4_checkambig(self):
47 def atomicwrite(checkambig):
48 f = atomictempfile('foo', checkambig=checkambig)
49 f.write('FOO')
50 f.close()
51
52 # try some times, because reproduction of ambiguity depends on
53 # "filesystem time"
54 for i in xrange(5):
55 atomicwrite(False)
56 oldstat = os.stat('foo')
57 if oldstat.st_ctime != oldstat.st_mtime:
58 # subsequent changing never causes ambiguity
59 continue
60
61 repetition = 3
62
63 # repeat atomic write with checkambig=True, to examine
64 # whether st_mtime is advanced multiple times as expecetd
65 for j in xrange(repetition):
66 atomicwrite(True)
67 newstat = os.stat('foo')
68 if oldstat.st_ctime != newstat.st_ctime:
69 # timestamp ambiguity was naturally avoided while repetition
70 continue
71
72 # st_mtime should be advanced "repetition" times, because
73 # all atomicwrite() occured at same time (in sec)
74 self.assertTrue(newstat.st_mtime ==
75 ((oldstat.st_mtime + repetition) & 0x7fffffff))
76 # no more examination is needed, if assumption above is true
77 break
78 else:
79 # This platform seems too slow to examine anti-ambiguity
80 # of file timestamp (or test happened to be executed at
81 # bad timing). Exit silently in this case, because running
82 # on other faster platforms can detect problems
83 pass
84
45 if __name__ == '__main__':
85 if __name__ == '__main__':
46 import silenttestrunner
86 import silenttestrunner
47 silenttestrunner.main(__name__)
87 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now