diff --git a/src/ircdb.py b/src/ircdb.py index b570352f7..26e3c95fa 100644 --- a/src/ircdb.py +++ b/src/ircdb.py @@ -620,64 +620,27 @@ class DuplicateHostmask(ValueError): class UsersDictionary(utils.IterableMap): """A simple serialized-to-file User Database.""" - __slots__ = ('noFlush', 'filename', 'users', '_nameCache', - '_hostmaskCache') + __slots__ = ('filename', 'users', '_nameCache', '_hostmaskCache') def __init__(self): - self.noFlush = False self.filename = None + self._preserver = unpreserve.Preserver(self.__class__.__name__) self.users = {} self.nextId = 0 self._nameCache = utils.structures.CacheDict(1000) self._hostmaskCache = utils.structures.CacheDict(1000) - # This is separate because the Creator has to access our instance. def open(self, filename): self.filename = filename - reader = unpreserve.Reader(IrcUserCreator, self) - try: - self.noFlush = True - try: - reader.readFile(filename) - self.noFlush = False - self.flush() - except EnvironmentError as e: - log.error('Invalid user dictionary file, resetting to empty.') - log.error('Exact error: %s', utils.exnToString(e)) - except Exception as e: - log.exception('Exact error:') - finally: - self.noFlush = False - - def reload(self): - """Reloads the database from its file.""" - self.nextId = 0 - self.users.clear() - self._nameCache.clear() - self._hostmaskCache.clear() - if self.filename is not None: - try: - self.open(self.filename) - except EnvironmentError as e: - log.warning('UsersDictionary.reload failed: %s', e) - else: - log.error('UsersDictionary.reload called with no filename.') + self._preserver.open(filename, IrcUserCreator, self) def flush(self): """Flushes the database to its file.""" - if not self.noFlush: - if self.filename is not None: - L = list(self.users.items()) - L.sort() - fd = utils.file.AtomicFile(self.filename) - for (id, u) in L: - fd.write('user %s' % id) - fd.write(os.linesep) - u.preserve(fd, indent=' ') - fd.close() - else: - log.error('UsersDictionary.flush called with no filename.') + if self.filename is not None: + L = sorted(self.users.items()) + blocks = [('user %s' % id, user) for (id, user) in L] + self._preserver.flush(self.filename, blocks) else: - log.debug('Not flushing UsersDictionary because of noFlush.') + log.error('UsersDictionary.flush called with no filename.') def close(self): self.flush() @@ -685,6 +648,17 @@ class UsersDictionary(utils.IterableMap): world.flushers.remove(self.flush) self.users.clear() + def reload(self): + """Reloads the database from its file.""" + if self.filename is not None: + self.nextId = 0 + self.users.clear() + self._nameCache.clear() + self._hostmaskCache.clear() + self.open(self.filename) + else: + log.error('UsersDictionary.reload called with no filename.') + def items(self): return self.users.items() @@ -832,44 +806,24 @@ class UsersDictionary(utils.IterableMap): class ChannelsDictionary(utils.IterableMap): - __slots__ = ('noFlush', 'filename', 'channels') + __slots__ = ('channels', 'filename') def __init__(self): - self.noFlush = False self.filename = None + self._preserver = unpreserve.Preserver(self.__class__.__name__) self.channels = ircutils.IrcDict() def open(self, filename): - self.noFlush = True - try: - self.filename = filename - reader = unpreserve.Reader(IrcChannelCreator, self) - try: - reader.readFile(filename) - self.noFlush = False - self.flush() - except EnvironmentError as e: - log.error('Invalid channel database, resetting to empty.') - log.error('Exact error: %s', utils.exnToString(e)) - except Exception as e: - log.error('Invalid channel database, resetting to empty.') - log.exception('Exact error:') - finally: - self.noFlush = False + self.filename = filename + self._preserver.open(filename, IrcChannelCreator, self) def flush(self): """Flushes the channel database to its file.""" - if not self.noFlush: - if self.filename is not None: - fd = utils.file.AtomicFile(self.filename) - for (channel, c) in self.channels.items(): - fd.write('channel %s' % channel) - fd.write(os.linesep) - c.preserve(fd, indent=' ') - fd.close() - else: - log.warning('ChannelsDictionary.flush without self.filename.') + if self.filename is not None: + L = sorted(self.channels.items()) + blocks = [('channel %s' % name, channel) for (name, channel) in L] + self._preserver.flush(self.filename, blocks) else: - log.debug('Not flushing ChannelsDictionary because of noFlush.') + log.error('UsersDictionary.flush called with no filename.') def close(self): self.flush() @@ -881,10 +835,7 @@ class ChannelsDictionary(utils.IterableMap): """Reloads the channel database from its file.""" if self.filename is not None: self.channels.clear() - try: - self.open(self.filename) - except EnvironmentError as e: - log.warning('ChannelsDictionary.reload failed: %s', e) + self.open(self.filename) else: log.warning('ChannelsDictionary.reload without self.filename.') diff --git a/src/unpreserve.py b/src/unpreserve.py index 434b80a47..9c42dae5b 100644 --- a/src/unpreserve.py +++ b/src/unpreserve.py @@ -27,6 +27,10 @@ # POSSIBILITY OF SUCH DAMAGE. ### +import os + +from . import log, utils + class Reader(object): """Opens a file and reads it in blocks, using the `Creator` class to instantiate an object for each of the blocks. @@ -100,5 +104,40 @@ class Reader(object): self.creator.finish() +class Preserver(object): + __slots__ = ('_class_name','noFlush') + + def __init__(self, class_name): + self._class_name = class_name + self.noFlush = False + + def open(self, filename, Creator, *args, **kwargs): + """Opens the `filename` and instantiates objects using the provided + `Creator` and args (see the `Reader` class).""" + try: + reader = Reader(Creator, *args, **kwargs) + try: + self.noFlush = True + reader.readFile(filename) + finally: + self.noFlush = False + self.flush() + except (EnvironmentError, Exception) as e: + log.exception('Invalid %s file, resetting to empty.', + self._class_name) + + def flush(self, filename, blocks): + if not self.noFlush: + fd = utils.file.AtomicFile(filename) + for (first_line, object_) in blocks: + fd.write(first_line) + fd.write(os.linesep) + object_.preserve(fd, indent=' ') + fd.close() + else: + log.debug('Not flushing %s because of noFlush.', + self._class_name) + + # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: