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) irc.addCallback(callback)
conf.registerGroup(conf.supybot, 'commands') conf.registerGroup(conf.supybot, 'commands')
conf.registerGroup(conf.supybot.commands, 'defaultPlugins', conf.registerGlobalValue(conf.supybot.commands, 'defaultPlugins',
registry.GroupWithDefault(registry.String('(Unused)', """Determines what registry.String('(Unused)', """Determines what commands have default
commands have default plugins set, and which plugins are set to be the plugins set, and which plugins are set to be the default for each of
default for each of those commands."""))) those commands."""))
conf.registerGlobalValue(conf.supybot.commands.defaultPlugins, conf.registerGlobalValue(conf.supybot.commands.defaultPlugins,
'list', registry.String('Misc', '')) 'list', registry.String('Misc', ''))
conf.registerGlobalValue(conf.supybot.commands.defaultPlugins, conf.registerGlobalValue(conf.supybot.commands.defaultPlugins,

View File

@ -57,21 +57,20 @@ supybot = registry.Group()
supybot.setName('supybot') supybot.setName('supybot')
def registerPlugin(name, currentValue=None): def registerPlugin(name, currentValue=None):
supybot.plugins.registerGroup( supybot.plugins.register(name, registry.Boolean(False, """Determines
name, whether this plugin is loaded by default."""))
registry.GroupWithValue(registry.Boolean(False, """Determines whether
this plugin is loaded by default.""")))
if currentValue is not None: if currentValue is not None:
supybot.plugins.getChild(name).setValue(currentValue) supybot.plugins.get(name).setValue(currentValue)
def registerChannelValue(group, name, value): def registerChannelValue(group, name, value):
group.registerGroup(name, registry.GroupWithDefault(value)) value.supplyDefault = True
group.register(name, value)
def registerGlobalValue(group, name, value): def registerGlobalValue(group, name, value):
group.registerGroup(name, registry.GroupWithValue(value)) group.register(name, value)
def registerGroup(group, name, Group=None): def registerGroup(Group, name, group=None):
group.registerGroup(name, Group) Group.register(name, group)
class ValidNick(registry.String): class ValidNick(registry.String):
def setValue(self, v): def setValue(self, v):
@ -98,13 +97,13 @@ supybot.register('ident', ValidNick('supybot',
supybot.register('user', registry.String('supybot', """Determines the user supybot.register('user', registry.String('supybot', """Determines the user
the bot sends to the server.""")) 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. # TODO: Make this check for validity.
supybot.register('server', registry.String('irc.freenode.net', """Determines supybot.register('server', registry.String('irc.freenode.net', """Determines
what server the bot connects to.""")) 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): class SpaceSeparatedListOfChannels(registry.SeparatedListOf):
Value = ValidChannel Value = ValidChannel
def splitter(self, s): def splitter(self, s):
@ -114,9 +113,9 @@ class SpaceSeparatedListOfChannels(registry.SeparatedListOf):
supybot.register('channels', SpaceSeparatedListOfChannels(['#supybot'], """ supybot.register('channels', SpaceSeparatedListOfChannels(['#supybot'], """
Determines what channels the bot will join when it connects to the server.""")) Determines what channels the bot will join when it connects to the server."""))
supybot.registerGroup('databases') supybot.register('databases')
supybot.databases.registerGroup('users') supybot.databases.register('users')
supybot.databases.registerGroup('channels') supybot.databases.register('channels')
supybot.databases.users.register('filename', registry.String('users.conf', """ supybot.databases.users.register('filename', registry.String('users.conf', """
Determines what filename will be used for the users database. This file will Determines what filename will be used for the users database. This file will
go into the directory specified by the supybot.directories.conf 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 will go into the directory specified by the supybot.directories.conf
variable.""")) variable."""))
supybot.registerGroup('directories') supybot.register('directories')
supybot.directories.register('conf', registry.String('conf', """ supybot.directories.register('conf', registry.String('conf', """
Determines what directory configuration data is put into.""")) Determines what directory configuration data is put into."""))
supybot.directories.register('data', registry.String('data', """ supybot.directories.register('data', registry.String('data', """
@ -175,7 +174,7 @@ bytes."""))
### ###
# Reply/error tweaking. # Reply/error tweaking.
### ###
supybot.registerGroup('reply') supybot.register('reply')
supybot.reply.register('oneToOne', registry.Boolean(True, """Determines whether supybot.reply.register('oneToOne', registry.Boolean(True, """Determines whether
the bot will send multi-message replies in a single messsage or in multiple 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 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 # Replies
### ###
supybot.registerGroup('replies') supybot.register('replies')
registerChannelValue(supybot.replies, 'error', registerChannelValue(supybot.replies, 'error',
registry.NormalizedString("""An error has occurred and has been logged. 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. # Driver stuff.
### ###
supybot.registerGroup('drivers') supybot.register('drivers')
supybot.drivers.register('poll', registry.Float(1.0, """Determines the default supybot.drivers.register('poll', registry.Float(1.0, """Determines the default
length of time a driver should block waiting for input.""")) 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 stable and simple, and if you've got Twisted installed, is probably your best
bet.""")) 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 conf.supybot.directories.register('log', registry.String('logs', """Determines
what directory the bot will store its logfiles in.""")) 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, conf.supybot.log.register('level', LogLevel(logging.INFO,
"""Determines what the minimum priority level logged will be. Valid values are """Determines what the minimum priority level logged will be. Valid values are
DEBUG, INFO, WARNING, ERROR, and CRITICAL, in order of increasing 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 informative (and thus more useful for debugging) they also take a significantly
greater amount of space in the logs. Hopefully, however, such uncaught greater amount of space in the logs. Hopefully, however, such uncaught
exceptions aren't very common.""")) exceptions aren't very common."""))
conf.supybot.log.registerGroup('stdout', conf.supybot.log.register('stdout',
registry.GroupWithValue(registry.Boolean(True, """Determines whether the bot registry.Boolean(True, """Determines whether the bot will log to
will log to stdout."""))) stdout."""))
class BooleanRequiredFalseOnWindows(registry.Boolean): class BooleanRequiredFalseOnWindows(registry.Boolean):
def set(self, s): def set(self, s):

View File

@ -93,12 +93,114 @@ def close(registry, filename, annotated=True):
fd.close() fd.close()
class Value(object): class Group(object):
def __init__(self, default, help): 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.default = default
self.help = utils.normalizeWhitespace(help.strip()) self.help = utils.normalizeWhitespace(help.strip())
self.setValue(default) 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): def set(self, s):
"""Override this with a function to convert a string to whatever type """Override this with a function to convert a string to whatever type
you want, and call self.setValue to set the value.""" 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) # This is simply prettier than naming this function get(self)
def __call__(self): def __call__(self):
# TODO: Check _lastModified to see if stuff needs to be reloaded from if _lastModified > self._lastModified:
# the _cache. if self.name in _cache:
self.set(_cache[self.name.lower()])
return self.value return self.value
class Boolean(Value): class Boolean(Value):
@ -274,157 +377,9 @@ class CommaSeparatedListOfStrings(SeparatedListOf):
class CommaSeparatedSetOfStrings(CommaSeparatedListOfStrings): class CommaSeparatedSetOfStrings(CommaSeparatedListOfStrings):
List = sets.Set 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 __name__ == '__main__':
#if 1:
import sys import sys
sys.setrecursionlimit(40) sys.setrecursionlimit(40)
supybot = Group() supybot = Group()
@ -432,20 +387,18 @@ if __name__ == '__main__':
supybot.register('throttleTime', Float(1, """Determines the minimum supybot.register('throttleTime', Float(1, """Determines the minimum
number of seconds the bot will wait between sending messages to the server. number of seconds the bot will wait between sending messages to the server.
""")) """))
supybot.registerGroup('plugins') supybot.register('plugins')
supybot.plugins.registerGroup('topic') supybot.plugins.register('topic')
supybot.plugins.topic.registerGroup('separator', supybot.plugins.topic.register('separator',
GroupWithDefault(StringSurroundedBySpaces(' || ', StringSurroundedBySpaces(' || ', """Determines what separator the bot
'Determines what separator the bot uses to separate topic entries.'))) uses to separate topic entries.""", supplyDefault=True))
supybot.plugins.topic.separator.get('#supybot').set(' |||| ') supybot.plugins.topic.separator.get('#supybot').set(' |||| ')
supybot.plugins.topic.separator.set(' <> ') supybot.plugins.topic.separator.set(' <> ')
supybot.throttleTime.set(10) supybot.throttleTime.set(10)
supybot.registerGroup('log') supybot.register('log')
supybot.log.registerGroup('stdout', supybot.log.register('stdout', Boolean(False, """Help for stdout."""))
GroupWithValue(Boolean(False,
"""Help for stdout.""")))
supybot.log.stdout.register('colorized', Boolean(False, supybot.log.stdout.register('colorized', Boolean(False,
'Help colorized')) 'Help colorized'))
supybot.log.stdout.setValue(True) supybot.log.stdout.setValue(True)