mirror of https://github.com/Mikaela/Limnoria.git
Deduplicate (de)serialization code shared by UsersDictionary and ChannelsDictionary.
This commit is contained in:
parent
7d218ec8ce
commit
a4f8e3f647
107
src/ircdb.py
107
src/ircdb.py
|
@ -620,64 +620,27 @@ class DuplicateHostmask(ValueError):
|
||||||
|
|
||||||
class UsersDictionary(utils.IterableMap):
|
class UsersDictionary(utils.IterableMap):
|
||||||
"""A simple serialized-to-file User Database."""
|
"""A simple serialized-to-file User Database."""
|
||||||
__slots__ = ('noFlush', 'filename', 'users', '_nameCache',
|
__slots__ = ('filename', 'users', '_nameCache', '_hostmaskCache')
|
||||||
'_hostmaskCache')
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.noFlush = False
|
|
||||||
self.filename = None
|
self.filename = None
|
||||||
|
self._preserver = unpreserve.Preserver(self.__class__.__name__)
|
||||||
self.users = {}
|
self.users = {}
|
||||||
self.nextId = 0
|
self.nextId = 0
|
||||||
self._nameCache = utils.structures.CacheDict(1000)
|
self._nameCache = utils.structures.CacheDict(1000)
|
||||||
self._hostmaskCache = 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):
|
def open(self, filename):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
reader = unpreserve.Reader(IrcUserCreator, self)
|
self._preserver.open(filename, 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.')
|
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
"""Flushes the database to its file."""
|
"""Flushes the database to its file."""
|
||||||
if not self.noFlush:
|
if self.filename is not None:
|
||||||
if self.filename is not None:
|
L = sorted(self.users.items())
|
||||||
L = list(self.users.items())
|
blocks = [('user %s' % id, user) for (id, user) in L]
|
||||||
L.sort()
|
self._preserver.flush(self.filename, blocks)
|
||||||
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.')
|
|
||||||
else:
|
else:
|
||||||
log.debug('Not flushing UsersDictionary because of noFlush.')
|
log.error('UsersDictionary.flush called with no filename.')
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.flush()
|
self.flush()
|
||||||
|
@ -685,6 +648,17 @@ class UsersDictionary(utils.IterableMap):
|
||||||
world.flushers.remove(self.flush)
|
world.flushers.remove(self.flush)
|
||||||
self.users.clear()
|
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):
|
def items(self):
|
||||||
return self.users.items()
|
return self.users.items()
|
||||||
|
|
||||||
|
@ -832,44 +806,24 @@ class UsersDictionary(utils.IterableMap):
|
||||||
|
|
||||||
|
|
||||||
class ChannelsDictionary(utils.IterableMap):
|
class ChannelsDictionary(utils.IterableMap):
|
||||||
__slots__ = ('noFlush', 'filename', 'channels')
|
__slots__ = ('channels', 'filename')
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.noFlush = False
|
|
||||||
self.filename = None
|
self.filename = None
|
||||||
|
self._preserver = unpreserve.Preserver(self.__class__.__name__)
|
||||||
self.channels = ircutils.IrcDict()
|
self.channels = ircutils.IrcDict()
|
||||||
|
|
||||||
def open(self, filename):
|
def open(self, filename):
|
||||||
self.noFlush = True
|
self.filename = filename
|
||||||
try:
|
self._preserver.open(filename, IrcChannelCreator, self)
|
||||||
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
|
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
"""Flushes the channel database to its file."""
|
"""Flushes the channel database to its file."""
|
||||||
if not self.noFlush:
|
if self.filename is not None:
|
||||||
if self.filename is not None:
|
L = sorted(self.channels.items())
|
||||||
fd = utils.file.AtomicFile(self.filename)
|
blocks = [('channel %s' % name, channel) for (name, channel) in L]
|
||||||
for (channel, c) in self.channels.items():
|
self._preserver.flush(self.filename, blocks)
|
||||||
fd.write('channel %s' % channel)
|
|
||||||
fd.write(os.linesep)
|
|
||||||
c.preserve(fd, indent=' ')
|
|
||||||
fd.close()
|
|
||||||
else:
|
|
||||||
log.warning('ChannelsDictionary.flush without self.filename.')
|
|
||||||
else:
|
else:
|
||||||
log.debug('Not flushing ChannelsDictionary because of noFlush.')
|
log.error('UsersDictionary.flush called with no filename.')
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.flush()
|
self.flush()
|
||||||
|
@ -881,10 +835,7 @@ class ChannelsDictionary(utils.IterableMap):
|
||||||
"""Reloads the channel database from its file."""
|
"""Reloads the channel database from its file."""
|
||||||
if self.filename is not None:
|
if self.filename is not None:
|
||||||
self.channels.clear()
|
self.channels.clear()
|
||||||
try:
|
self.open(self.filename)
|
||||||
self.open(self.filename)
|
|
||||||
except EnvironmentError as e:
|
|
||||||
log.warning('ChannelsDictionary.reload failed: %s', e)
|
|
||||||
else:
|
else:
|
||||||
log.warning('ChannelsDictionary.reload without self.filename.')
|
log.warning('ChannelsDictionary.reload without self.filename.')
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
# POSSIBILITY OF SUCH DAMAGE.
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
###
|
###
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from . import log, utils
|
||||||
|
|
||||||
class Reader(object):
|
class Reader(object):
|
||||||
"""Opens a file and reads it in blocks, using the `Creator` class to
|
"""Opens a file and reads it in blocks, using the `Creator` class to
|
||||||
instantiate an object for each of the blocks.
|
instantiate an object for each of the blocks.
|
||||||
|
@ -100,5 +104,40 @@ class Reader(object):
|
||||||
self.creator.finish()
|
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:
|
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue