Refactored registry to remove the Group/Value distinction.

This commit is contained in:
Jeremy Fincher 2004-02-03 16:43:22 +00:00
parent 18ce33a61d
commit 9bf4f35a51
4 changed files with 142 additions and 190 deletions

View File

@ -95,10 +95,10 @@ def loadPluginClass(irc, module):
irc.addCallback(callback)
conf.registerGroup(conf.supybot, 'commands')
conf.registerGroup(conf.supybot.commands, 'defaultPlugins',
registry.GroupWithDefault(registry.String('(Unused)', """Determines what
commands have default plugins set, and which plugins are set to be the
default for each of those commands.""")))
conf.registerGlobalValue(conf.supybot.commands, 'defaultPlugins',
registry.String('(Unused)', """Determines what commands have default
plugins set, and which plugins are set to be the default for each of
those commands."""))
conf.registerGlobalValue(conf.supybot.commands.defaultPlugins,
'list', registry.String('Misc', ''))
conf.registerGlobalValue(conf.supybot.commands.defaultPlugins,

View File

@ -57,21 +57,20 @@ supybot = registry.Group()
supybot.setName('supybot')
def registerPlugin(name, currentValue=None):
supybot.plugins.registerGroup(
name,
registry.GroupWithValue(registry.Boolean(False, """Determines whether
this plugin is loaded by default.""")))
supybot.plugins.register(name, registry.Boolean(False, """Determines
whether this plugin is loaded by default."""))
if currentValue is not None:
supybot.plugins.getChild(name).setValue(currentValue)
supybot.plugins.get(name).setValue(currentValue)
def registerChannelValue(group, name, value):
group.registerGroup(name, registry.GroupWithDefault(value))
value.supplyDefault = True
group.register(name, value)
def registerGlobalValue(group, name, value):
group.registerGroup(name, registry.GroupWithValue(value))
group.register(name, value)
def registerGroup(group, name, Group=None):
group.registerGroup(name, Group)
def registerGroup(Group, name, group=None):
Group.register(name, group)
class ValidNick(registry.String):
def setValue(self, v):
@ -98,13 +97,13 @@ supybot.register('ident', ValidNick('supybot',
supybot.register('user', registry.String('supybot', """Determines the user
the bot sends to the server."""))
supybot.register('password', registry.String('', """Determines the password to
be sent to the server if it requires one."""))
# TODO: Make this check for validity.
supybot.register('server', registry.String('irc.freenode.net', """Determines
what server the bot connects to."""))
supybot.register('password', registry.String('', """Determines the password to
be sent to the server if it requires one."""))
class SpaceSeparatedListOfChannels(registry.SeparatedListOf):
Value = ValidChannel
def splitter(self, s):
@ -114,9 +113,9 @@ class SpaceSeparatedListOfChannels(registry.SeparatedListOf):
supybot.register('channels', SpaceSeparatedListOfChannels(['#supybot'], """
Determines what channels the bot will join when it connects to the server."""))
supybot.registerGroup('databases')
supybot.databases.registerGroup('users')
supybot.databases.registerGroup('channels')
supybot.register('databases')
supybot.databases.register('users')
supybot.databases.register('channels')
supybot.databases.users.register('filename', registry.String('users.conf', """
Determines what filename will be used for the users database. This file will
go into the directory specified by the supybot.directories.conf
@ -126,7 +125,7 @@ supybot.databases.channels.register('filename',registry.String('channels.conf',
will go into the directory specified by the supybot.directories.conf
variable."""))
supybot.registerGroup('directories')
supybot.register('directories')
supybot.directories.register('conf', registry.String('conf', """
Determines what directory configuration data is put into."""))
supybot.directories.register('data', registry.String('data', """
@ -175,7 +174,7 @@ bytes."""))
###
# Reply/error tweaking.
###
supybot.registerGroup('reply')
supybot.register('reply')
supybot.reply.register('oneToOne', registry.Boolean(True, """Determines whether
the bot will send multi-message replies in a single messsage or in multiple
messages. For safety purposes (so the bot can't possibly flood) it will
@ -261,7 +260,7 @@ why these default to what they do."""))
###
# Replies
###
supybot.registerGroup('replies')
supybot.register('replies')
registerChannelValue(supybot.replies, 'error',
registry.NormalizedString("""An error has occurred and has been logged.
@ -379,7 +378,7 @@ there are no prefix characters set, it just uses its nick."""))
###
# Driver stuff.
###
supybot.registerGroup('drivers')
supybot.register('drivers')
supybot.drivers.register('poll', registry.Float(1.0, """Determines the default
length of time a driver should block waiting for input."""))
@ -407,7 +406,7 @@ you to integrate with asyncore-based applications. twistedDrivers is very
stable and simple, and if you've got Twisted installed, is probably your best
bet."""))
supybot.registerGroup('plugins') # This will be used by plugins, but not here.
supybot.register('plugins') # This will be used by plugins, but not here.
###############################
###############################

View File

@ -193,7 +193,7 @@ class LogLevel(registry.Value):
conf.supybot.directories.register('log', registry.String('logs', """Determines
what directory the bot will store its logfiles in."""))
conf.supybot.registerGroup('log')
conf.supybot.register('log')
conf.supybot.log.register('level', LogLevel(logging.INFO,
"""Determines what the minimum priority level logged will be. Valid values are
DEBUG, INFO, WARNING, ERROR, and CRITICAL, in order of increasing
@ -207,9 +207,9 @@ Determines whether highly detailed tracebacks will be logged. While more
informative (and thus more useful for debugging) they also take a significantly
greater amount of space in the logs. Hopefully, however, such uncaught
exceptions aren't very common."""))
conf.supybot.log.registerGroup('stdout',
registry.GroupWithValue(registry.Boolean(True, """Determines whether the bot
will log to stdout.""")))
conf.supybot.log.register('stdout',
registry.Boolean(True, """Determines whether the bot will log to
stdout."""))
class BooleanRequiredFalseOnWindows(registry.Boolean):
def set(self, s):

View File

@ -93,12 +93,114 @@ def close(registry, filename, annotated=True):
fd.close()
class Value(object):
def __init__(self, default, help):
class Group(object):
def __init__(self, supplyDefault=False):
self.name = 'unset'
self.added = []
self.children = {}
self.originals = {}
self._lastModified = 0
self.supplyDefault = supplyDefault
OriginalClass = self.__class__
class X(OriginalClass):
"""This class exists to differentiate those values that have
been changed from their default from those that haven't."""
def set(self, *args):
self.__class__ = OriginalClass
self.set(*args)
def setValue(self, *args):
self.__class__ = OriginalClass
self.setValue(*args)
self.X = X
def __nonExistentEntry(self, attr):
s = '%s is not a valid entry in %s' % (attr, self.name)
raise NonExistentRegistryEntry, s
def __normalizeAttr(self, attr):
return attr.lower()
def __makeChild(self, attr, s):
v = self.__class__(self.default, self.help)
v.set(s)
v.__class__ = self.X
v.supplyDefault = False
self.register(attr, v)
return v
def __getattr__(self, attr):
original = attr
attr = self.__normalizeAttr(attr)
if attr in self.children:
return self.children[attr]
elif self.supplyDefault:
return self.__makeChild(original, str(self))
else:
self.__nonExistentEntry(original)
def get(self, attr):
# Not getattr(self, attr) because some nodes might have groups that
# are named the same as their methods.
return self.__getattr__(attr)
def setName(self, name):
self.name = name
if name in _cache and self._lastModified < _lastModified:
self.set(_cache[name.lower()])
if self.supplyDefault:
for (k, v) in _cache.iteritems():
if k.startswith(self.name):
(_, group) = rsplit(k, '.', 1)
self.__makeChild(group, v)
def register(self, name, node=None):
original = name
name = self.__normalizeAttr(name)
if node is None:
node = Group()
if name not in self.children: # XXX Is this right?
self.children[name] = node
self.added.append(original)
self.originals[name] = original
fullname = '%s.%s' % (self.name, original)
node.setName(fullname)
def unregister(self, name):
original = name
name = self.__normalizeAttr(name)
try:
del self.children[name]
self.added.remove(original)
except KeyError:
self.__nonExistentEntry(original)
def getValues(self, getChildren=False, fullNames=True):
L = []
for name in map(self.__normalizeAttr, self.added):
node = self.children[name]
if hasattr(node, 'value'):
if node.__class__ is not self.X:
L.append((node.name, node))
if getChildren:
L.extend(node.getValues(getChildren, fullNames))
if not fullNames:
L = [(rsplit(s, '.', 1)[1], node) for (s, node) in L]
return L
class Value(Group):
def __init__(self, default, help, **kwargs):
Group.__init__(self, **kwargs)
self.default = default
self.help = utils.normalizeWhitespace(help.strip())
self.setValue(default)
def setName(self, *args):
if self.name == 'unset':
self._lastModified = 0
Group.setName(self, *args)
self._lastModified = time.time()
def set(self, s):
"""Override this with a function to convert a string to whatever type
you want, and call self.setValue to set the value."""
@ -118,8 +220,9 @@ class Value(object):
# This is simply prettier than naming this function get(self)
def __call__(self):
# TODO: Check _lastModified to see if stuff needs to be reloaded from
# the _cache.
if _lastModified > self._lastModified:
if self.name in _cache:
self.set(_cache[self.name.lower()])
return self.value
class Boolean(Value):
@ -274,157 +377,9 @@ class CommaSeparatedListOfStrings(SeparatedListOf):
class CommaSeparatedSetOfStrings(CommaSeparatedListOfStrings):
List = sets.Set
class Group(object):
def __init__(self):
self.name = 'unset'
self.values = {}
self.children = {}
self.originals = {}
def __nonExistentEntry(self, attr):
s = '%s is not a valid entry in %s' % (attr, self.name)
raise NonExistentRegistryEntry, s
def __getattr__(self, attr):
original = attr
attr = attr.lower()
if attr in self.values:
return self.values[attr]
elif attr in self.children:
return self.children[attr]
else:
self.__nonExistentEntry(original)
def get(self, attr):
return self.__getattr__(attr)
def getChild(self, attr):
return self.children[attr.lower()]
def setName(self, name):
self.name = name
def getName(self):
return self.name
def register(self, name, value):
original = name
name = name.lower()
self.values[name] = value
self.originals[name] = original
if _cache:
fullname = '%s.%s' % (self.name, name)
if fullname in _cache:
value.set(_cache[fullname])
def registerGroup(self, name, group=None):
original = name
name = name.lower()
if name in self.children:
return # Ignore redundant group inserts.
if group is None:
group = Group()
self.children[name] = group
self.originals[name] = original
fullname = '%s.%s' % (self.name, name)
group.setName(fullname)
if _cache and fullname in _cache:
group.set(_cache[fullname])
def getValues(self, getChildren=False, fullNames=True):
L = []
items = self.values.items()
# If getChildren=True, the group will insert itself into its getValues.
if not getChildren:
for (name, child) in self.children.items():
if hasattr(child, 'value'):
items.append((name, child))
utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
for (name, value) in items:
if fullNames:
name = '%s.%s' % (self.getName(), self.originals[name])
else:
name = self.originals[name]
L.append((name, value))
if getChildren:
items = self.children.items()
utils.sortBy(lambda (k, _): (k.lower(), len(k), k), items)
for (_, child) in items:
L.extend(child.getValues(getChildren, fullNames))
return L
class GroupWithValue(Group):
def __init__(self, value):
Group.__init__(self)
self.value = value
self.help = value.help
self.default = value.default
def set(self, s):
self.value.set(s)
def setValue(self, v):
self.value.setValue(v)
def __call__(self):
return self.value()
def __str__(self):
return str(self.value)
def getValues(self, getChildren=False, fullNames=True):
L = Group.getValues(self, getChildren, fullNames)
if getChildren:
L.insert(0, (self.getName(), self))
return L
class GroupWithDefault(GroupWithValue):
def __init__(self, value):
class X(value.__class__):
"""This class exists to differentiate those values that have been
changed from their default to those that haven't."""
def set(self, *args):
self.__class__ = value.__class__
self.set(*args)
def setValue(self, *args):
self.__class__ = value.__class__
self.setValue(*args)
self.X = X
GroupWithValue.__init__(self, value)
def __makeChild(self, attr, s):
#print '***', attr, ':', repr(s)
v = copy.copy(self.value)
v.set(s)
v.__class__ = self.X
self.register(attr, v)
return v
def __getattr__(self, attr):
try:
v = GroupWithValue.__getattr__(self, attr)
if v.__class__ is self.X:
raise NonExistentRegistryEntry
except NonExistentRegistryEntry:
v = self.__makeChild(attr, str(self))
return v
def setName(self, name):
GroupWithValue.setName(self, name)
for (k, v) in _cache.iteritems():
if k.startswith(self.name):
(_, group) = rsplit(k, '.', 1)
self.__makeChild(group, v)
def getValues(self, getChildren=False, fullNames=True):
L = GroupWithValue.getValues(self, getChildren, fullNames)
L = [v for v in L if v[1].__class__ is not self.X]
return L
if __name__ == '__main__':
#if 1:
import sys
sys.setrecursionlimit(40)
supybot = Group()
@ -432,20 +387,18 @@ if __name__ == '__main__':
supybot.register('throttleTime', Float(1, """Determines the minimum
number of seconds the bot will wait between sending messages to the server.
"""))
supybot.registerGroup('plugins')
supybot.plugins.registerGroup('topic')
supybot.plugins.topic.registerGroup('separator',
GroupWithDefault(StringSurroundedBySpaces(' || ',
'Determines what separator the bot uses to separate topic entries.')))
supybot.register('plugins')
supybot.plugins.register('topic')
supybot.plugins.topic.register('separator',
StringSurroundedBySpaces(' || ', """Determines what separator the bot
uses to separate topic entries.""", supplyDefault=True))
supybot.plugins.topic.separator.get('#supybot').set(' |||| ')
supybot.plugins.topic.separator.set(' <> ')
supybot.throttleTime.set(10)
supybot.registerGroup('log')
supybot.log.registerGroup('stdout',
GroupWithValue(Boolean(False,
"""Help for stdout.""")))
supybot.register('log')
supybot.log.register('stdout', Boolean(False, """Help for stdout."""))
supybot.log.stdout.register('colorized', Boolean(False,
'Help colorized'))
supybot.log.stdout.setValue(True)