mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-07 19:49:23 +01:00
Added utils.AtomicFile and converted our uses of 'w' to it. This rocks.
This commit is contained in:
parent
b2a94583f2
commit
823bfb040f
@ -104,7 +104,7 @@ class InfobotDB(object):
|
|||||||
'I hear ya']
|
'I hear ya']
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
fd = file(filename, 'w')
|
fd = utils.AtomicFile(filename)
|
||||||
pickle.dump((self._is, self._are), fd)
|
pickle.dump((self._is, self._are), fd)
|
||||||
fd.close()
|
fd.close()
|
||||||
|
|
||||||
|
@ -149,8 +149,7 @@ class URLDB(object):
|
|||||||
return [url for (url, nick) in self.getUrlsAndNicks(p)]
|
return [url for (url, nick) in self.getUrlsAndNicks(p)]
|
||||||
|
|
||||||
def vacuum(self):
|
def vacuum(self):
|
||||||
filename = utils.mktemp()
|
out = utils.AtomicFile(self.filename)
|
||||||
out = file(filename, 'w')
|
|
||||||
notAdded = 0
|
notAdded = 0
|
||||||
urls = self.getUrlsAndNicks(lambda *args: True)
|
urls = self.getUrlsAndNicks(lambda *args: True)
|
||||||
seen = sets.Set()
|
seen = sets.Set()
|
||||||
@ -165,7 +164,6 @@ class URLDB(object):
|
|||||||
if urlNick is not None:
|
if urlNick is not None:
|
||||||
out.write(self._formatRecord(*urlNick))
|
out.write(self._formatRecord(*urlNick))
|
||||||
out.close()
|
out.close()
|
||||||
shutil.move(filename, self.filename)
|
|
||||||
self.log.info('Vacuumed %s, removed %s records.',
|
self.log.info('Vacuumed %s, removed %s records.',
|
||||||
self.filename, notAdded)
|
self.filename, notAdded)
|
||||||
|
|
||||||
|
@ -221,9 +221,14 @@ class ChannelUserDB(ChannelUserDictionary):
|
|||||||
log.debug('Exception: %s', utils.exnToString(e))
|
log.debug('Exception: %s', utils.exnToString(e))
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
fd = file(self.filename, 'w')
|
fd = utils.AtomicFile(self.filename)
|
||||||
writer = csv.writer(fd)
|
writer = csv.writer(fd)
|
||||||
items = self.items()
|
items = self.items()
|
||||||
|
if not items:
|
||||||
|
log.warning('%s: Refusing to write blank file.',
|
||||||
|
self.__class__.__name__)
|
||||||
|
fd.rollback()
|
||||||
|
return
|
||||||
items.sort()
|
items.sort()
|
||||||
for ((channel, id), v) in items:
|
for ((channel, id), v) in items:
|
||||||
L = self.serialize(v)
|
L = self.serialize(v)
|
||||||
|
@ -136,7 +136,7 @@ def make(dbFilename, readFilename=None):
|
|||||||
class Maker(object):
|
class Maker(object):
|
||||||
"""Class for making CDB databases."""
|
"""Class for making CDB databases."""
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
self.fd = file(filename, 'w')
|
self.fd = utils.AtomicFile(filename)
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.fd.seek(2048)
|
self.fd.seek(2048)
|
||||||
self.hashPointers = [(0, 0)] * 256
|
self.hashPointers = [(0, 0)] * 256
|
||||||
|
@ -571,7 +571,7 @@ class UsersDictionary(utils.IterableMap):
|
|||||||
if self.filename is not None:
|
if self.filename is not None:
|
||||||
L = self.users.items()
|
L = self.users.items()
|
||||||
L.sort()
|
L.sort()
|
||||||
fd = file(self.filename, 'w')
|
fd = utils.AtomicFile(self.filename)
|
||||||
for (id, u) in L:
|
for (id, u) in L:
|
||||||
fd.write('user %s' % id)
|
fd.write('user %s' % id)
|
||||||
fd.write(os.linesep)
|
fd.write(os.linesep)
|
||||||
@ -732,7 +732,7 @@ class ChannelsDictionary(utils.IterableMap):
|
|||||||
def flush(self):
|
def flush(self):
|
||||||
"""Flushes the channel database to its file."""
|
"""Flushes the channel database to its file."""
|
||||||
if self.filename is not None:
|
if self.filename is not None:
|
||||||
fd = file(self.filename, 'w')
|
fd = utils.AtomicFile(self.filename)
|
||||||
for (channel, c) in self.channels.iteritems():
|
for (channel, c) in self.channels.iteritems():
|
||||||
fd.write('channel %s' % channel)
|
fd.write('channel %s' % channel)
|
||||||
fd.write(os.linesep)
|
fd.write(os.linesep)
|
||||||
@ -792,7 +792,7 @@ class IgnoresDB(object):
|
|||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
if self.filename is not None:
|
if self.filename is not None:
|
||||||
fd = file(self.filename, 'w')
|
fd = utils.AtomicFile(self.filename)
|
||||||
for hostmask in self.hostmasks:
|
for hostmask in self.hostmasks:
|
||||||
fd.write(hostmask)
|
fd.write(hostmask)
|
||||||
fd.write(os.linesep)
|
fd.write(os.linesep)
|
||||||
|
@ -472,7 +472,6 @@ class IrcString(str):
|
|||||||
x.lowered = toLower(x)
|
x.lowered = toLower(x)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
def __eq__(self, s):
|
def __eq__(self, s):
|
||||||
try:
|
try:
|
||||||
return toLower(s) == self.lowered
|
return toLower(s) == self.lowered
|
||||||
|
@ -78,7 +78,7 @@ def open(filename, clear=False):
|
|||||||
def close(registry, filename, annotated=True, helpOnceOnly=False):
|
def close(registry, filename, annotated=True, helpOnceOnly=False):
|
||||||
first = True
|
first = True
|
||||||
helpCache = sets.Set()
|
helpCache = sets.Set()
|
||||||
fd = file(filename, 'w')
|
fd = utils.AtomicFile(filename)
|
||||||
for (name, value) in registry.getValues(getChildren=True):
|
for (name, value) in registry.getValues(getChildren=True):
|
||||||
if annotated and hasattr(value,'help') and value.help:
|
if annotated and hasattr(value,'help') and value.help:
|
||||||
if not helpOnceOnly or value.help not in self.helpCache:
|
if not helpOnceOnly or value.help not in self.helpCache:
|
||||||
|
30
src/utils.py
30
src/utils.py
@ -676,18 +676,38 @@ def stackTrace():
|
|||||||
|
|
||||||
class AtomicFile(file):
|
class AtomicFile(file):
|
||||||
"""Used for files that need to be atomically written -- i.e., if there's a
|
"""Used for files that need to be atomically written -- i.e., if there's a
|
||||||
failure, the original file remains, unmodified."""
|
failure, the original file remains, unmodified.
|
||||||
def __init__(self, filename, flags='w'):
|
|
||||||
if flags not in ('a', 'w'):
|
Opens the file in 'w' mode."""
|
||||||
raise ValueError, 'AtomicFile should only be used for writing.'
|
def __init__(self, filename, allowEmptyOverwrite=False):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
self.rolledback = False
|
||||||
|
self.allowEmptyOverwrite = allowEmptyOverwrite
|
||||||
self.tempFilename = '%s.%s' % (filename, mktemp())
|
self.tempFilename = '%s.%s' % (filename, mktemp())
|
||||||
super(AtomicFile, self).__init__(self.tempFilename, flags)
|
super(AtomicFile, self).__init__(self.tempFilename, 'w')
|
||||||
|
|
||||||
|
def rollback(self):
|
||||||
|
#print 'AtomicFile.rollback'
|
||||||
|
super(AtomicFile, self).close()
|
||||||
|
if os.path.exists(self.tempFilename):
|
||||||
|
print 'AtomicFile: Removing %s.' % self.tempFilename
|
||||||
|
os.remove(self.tempFilename)
|
||||||
|
self.rolledback = True
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
#print 'AtomicFile.close'
|
||||||
|
if not self.rolledback:
|
||||||
|
#print 'AtomicFile.close: actually closing.'
|
||||||
super(AtomicFile, self).close()
|
super(AtomicFile, self).close()
|
||||||
|
size = os.stat(self.tempFilename).st_size
|
||||||
|
if size or self.allowEmptyOverwrite:
|
||||||
|
if os.path.exists(self.tempFilename):
|
||||||
shutil.move(self.tempFilename, self.filename)
|
shutil.move(self.tempFilename, self.filename)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
#print 'AtomicFile.__del__'
|
||||||
|
self.rollback()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys, doctest
|
import sys, doctest
|
||||||
doctest.testmod(sys.modules['__main__'])
|
doctest.testmod(sys.modules['__main__'])
|
||||||
|
Loading…
Reference in New Issue
Block a user