From 9bf4f35a51f8c0e6bb96f144dd3a7bba7d5d7188 Mon Sep 17 00:00:00 2001 From: Jeremy Fincher Date: Tue, 3 Feb 2004 16:43:22 +0000 Subject: [PATCH] Refactored registry to remove the Group/Value distinction. --- src/Owner.py | 8 +- src/conf.py | 39 ++++--- src/log.py | 8 +- src/registry.py | 277 ++++++++++++++++++++---------------------------- 4 files changed, 142 insertions(+), 190 deletions(-) diff --git a/src/Owner.py b/src/Owner.py index 3b24d7ffc..d5d2d7bfe 100644 --- a/src/Owner.py +++ b/src/Owner.py @@ -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, diff --git a/src/conf.py b/src/conf.py index c3eaba90e..4d4319c6f 100644 --- a/src/conf.py +++ b/src/conf.py @@ -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. ############################### ############################### diff --git a/src/log.py b/src/log.py index a717e3774..fa7cbd1fe 100644 --- a/src/log.py +++ b/src/log.py @@ -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): diff --git a/src/registry.py b/src/registry.py index f41c29baf..a6529ce63 100644 --- a/src/registry.py +++ b/src/registry.py @@ -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)