diff --git a/src/conf.py b/src/conf.py index 19f5b0711..33c88706e 100644 --- a/src/conf.py +++ b/src/conf.py @@ -134,6 +134,7 @@ def registerUserValue(group, name, value): class ValidNick(registry.String): """Value must be a valid IRC nick.""" + __slots__ = () def setValue(self, v): if not ircutils.isNick(v): self.error() @@ -142,6 +143,7 @@ class ValidNick(registry.String): class ValidNickOrEmpty(ValidNick): """Value must be a valid IRC nick or empty.""" + __slots__ = () def setValue(self, v): if v != '' and not ircutils.isNick(v): self.error() @@ -149,11 +151,13 @@ class ValidNickOrEmpty(ValidNick): registry.String.setValue(self, v) class ValidNicks(registry.SpaceSeparatedListOf): + __slots__ = () Value = ValidNick class ValidNickAllowingPercentS(ValidNick): """Value must be a valid IRC nick, with the possible exception of a %s in it.""" + __slots__ = () def setValue(self, v): # If this works, it's a valid nick, aside from the %s. try: @@ -164,10 +168,12 @@ class ValidNickAllowingPercentS(ValidNick): self.error() class ValidNicksAllowingPercentS(ValidNicks): + __slots__ = () Value = ValidNickAllowingPercentS class ValidChannel(registry.String): """Value must be a valid IRC channel name.""" + __slots__ = ('channel',) def setValue(self, v): self.channel = v if ',' in v: @@ -194,6 +200,7 @@ class ValidChannel(registry.String): class ValidHostmask(registry.String): """Value must be a valid user hostmask.""" + __slots__ = () def setValue(self, v): if not ircutils.isUserHostmask(v): self.error() @@ -219,6 +226,7 @@ registerGlobalValue(supybot, 'ident', # bots which are migrated from Supybot or an old version of Limnoria # (whose default value of supybot.user is the empty string). class VersionIfEmpty(registry.String): + __slots__ = () def __call__(self): ret = registry.String.__call__(self) if not ret: @@ -231,6 +239,7 @@ registerGlobalValue(supybot, 'user', will be generated if this is left empty."""))) class Networks(registry.SpaceSeparatedSetOfStrings): + __slots__ = () List = ircutils.IrcSet registerGlobalValue(supybot, 'networks', @@ -238,6 +247,7 @@ registerGlobalValue(supybot, 'networks', orderAlphabetically=True)) class Servers(registry.SpaceSeparatedListOfStrings): + __slots__ = () def normalize(self, s): if ':' not in s: s += ':6667' @@ -262,6 +272,7 @@ class Servers(registry.SpaceSeparatedListOfStrings): class SocksProxy(registry.String): """Value must be a valid hostname:port string.""" + __slots__ = () def setValue(self, v): # TODO: improve checks if ':' not in v: @@ -273,6 +284,7 @@ class SocksProxy(registry.String): super(SocksProxy, self).setValue(v) class SpaceSeparatedSetOfChannels(registry.SpaceSeparatedListOf): + __slots__ = () sorted = True List = ircutils.IrcSet Value = ValidChannel @@ -313,10 +325,12 @@ class SpaceSeparatedSetOfChannels(registry.SpaceSeparatedListOf): return None class ValidSaslMechanism(registry.OnlySomeStrings): + __slots__ = () validStrings = ('ecdsa-nist256p-challenge', 'external', 'plain', 'scram-sha-256') class SpaceSeparatedListOfSaslMechanisms(registry.SpaceSeparatedListOf): + __slots__ = () Value = ValidSaslMechanism def registerNetwork(name, password='', ssl=True, sasl_username='', @@ -554,6 +568,7 @@ registerChannelValue(supybot.reply, 'showSimpleSyntax', class ValidPrefixChars(registry.String): """Value must contain only ~!@#$%^&*()_-+=[{}]\\|'\";:,<.>/?""" + __slots__ = () def setValue(self, v): if any([x not in '`~!@#$%^&*()_-+=[{}]\\|\'";:,<.>/?' for x in v]): self.error() @@ -698,6 +713,7 @@ registerGroup(supybot, 'commands') class ValidQuotes(registry.Value): """Value must consist solely of \", ', and ` characters.""" + __slots__ = () def setValue(self, v): if [c for c in v if c not in '"`\'']: self.error() @@ -722,6 +738,7 @@ registerGlobalValue(supybot.commands.nested, 'maximum', commands more nested than this."""))) class ValidBrackets(registry.OnlySomeStrings): + __slots__ = () validStrings = ('', '[]', '<>', '{}', '()') registerChannelValue(supybot.commands.nested, 'brackets', @@ -816,6 +833,7 @@ registerGlobalValue(supybot.drivers, 'poll', driver should block waiting for input."""))) class ValidDriverModule(registry.OnlySomeStrings): + __slots__ = () validStrings = ('default', 'Socket', 'Twisted') registerGlobalValue(supybot.drivers, 'module', @@ -836,6 +854,7 @@ registerGlobalValue(supybot.drivers, 'maxReconnectWait', # XXX This shouldn't make directories willy-nilly. As it is now, if it's # configured, it'll still make the default directories, I think. class Directory(registry.String): + __slots__ = () def __call__(self): # ??? Should we perhaps always return an absolute path here? v = super(Directory, self).__call__() @@ -857,6 +876,7 @@ class Directory(registry.String): return os.path.join(myself, filename) class DataFilename(registry.String): + __slots__ = () def __call__(self): v = super(DataFilename, self).__call__() dataDir = supybot.directories.data() @@ -867,6 +887,7 @@ class DataFilename(registry.String): return v class DataFilenameDirectory(DataFilename, Directory): + __slots__ = () def __call__(self): v = DataFilename.__call__(self) v = Directory.__call__(self) @@ -924,6 +945,7 @@ registerGlobalValue(supybot.plugins, 'alwaysLoadImportant', # supybot.databases. For stuff relating to Supybot's databases (duh!) ### class Databases(registry.SpaceSeparatedListOfStrings): + __slots__ = () def __call__(self): v = super(Databases, self).__call__() if not v: @@ -976,6 +998,7 @@ registerGlobalValue(supybot.databases.channels, 'filename', # TODO This will need to do more in the future (such as making sure link.allow # will let the link occur), but for now let's just leave it as this. class ChannelSpecific(registry.Boolean): + __slots__ = () def getChannelLink(self, channel): channelSpecific = supybot.databases.plugins.channelSpecific channels = [channel] @@ -1029,6 +1052,7 @@ registerChannelValue(supybot.databases.plugins.channelSpecific.link, 'allow', class CDB(registry.Boolean): + __slots__ = () def connect(self, filename): from . import cdb basename = os.path.basename(filename) @@ -1069,6 +1093,7 @@ registerGroup(supybot, 'protocols') registerGroup(supybot.protocols, 'irc') class Banmask(registry.SpaceSeparatedSetOfStrings): + __slots__ = ('__parent', '__dict__') # __dict__ is needed to set __doc__ validStrings = ('exact', 'nick', 'user', 'host') def __init__(self, *args, **kwargs): assert self.validStrings, 'There must be some valid strings. ' \ @@ -1204,6 +1229,7 @@ registerGlobalValue(supybot.protocols.http, 'peekSize', class HttpProxy(registry.String): """Value must be a valid hostname:port string.""" + __slots__ = () def setValue(self, v): proxies = {} if v != "": @@ -1242,6 +1268,7 @@ registerGroup(supybot.servers, 'http') class IP(registry.String): """Value must be a valid IP.""" + __slots__ = () def setValue(self, v): if v and not utils.net.isIP(v): self.error() @@ -1249,6 +1276,7 @@ class IP(registry.String): registry.String.setValue(self, v) class ListOfIPs(registry.SpaceSeparatedListOfStrings): + __slots__ = () Value = IP registerGlobalValue(supybot.servers.http, 'singleStack', @@ -1295,6 +1323,7 @@ registerGlobalValue(supybot, 'externalIP', class SocketTimeout(registry.PositiveInteger): """Value must be an integer greater than supybot.drivers.poll and must be greater than or equal to 1.""" + __slots__ = () def setValue(self, v): if v < supybot.drivers.poll() or v < 1: self.error() diff --git a/src/ircdb.py b/src/ircdb.py index 0ada6b574..a16c9f129 100644 --- a/src/ircdb.py +++ b/src/ircdb.py @@ -108,6 +108,7 @@ def unWildcardHostmask(hostmask): _invert = invertCapability class CapabilitySet(set): """A subclass of set handling basic capability stuff.""" + __slots__ = ('__parent',) def __init__(self, capabilities=()): self.__parent = super(CapabilitySet, self) self.__parent.__init__() @@ -155,6 +156,7 @@ class CapabilitySet(set): antiOwner = makeAntiCapability('owner') class UserCapabilitySet(CapabilitySet): """A subclass of CapabilitySet to handle the owner capability correctly.""" + __slots__ = ('__parent',) def __init__(self, *args, **kwargs): self.__parent = super(UserCapabilitySet, self) self.__parent.__init__(*args, **kwargs) @@ -196,6 +198,8 @@ class UserCapabilitySet(CapabilitySet): class IrcUser(object): """This class holds the capabilities and authentications for a user.""" + __slots__ = ('id', 'auth', 'name', 'ignore', 'secure', 'hashed', + 'password', 'capabilities', 'hostmasks', 'nicks', 'gpgkeys') def __init__(self, ignore=False, password='', name='', capabilities=(), hostmasks=None, nicks=None, secure=False, hashed=False): @@ -368,6 +372,8 @@ class IrcUser(object): class IrcChannel(object): """This class holds the capabilities, bans, and ignores of a channel.""" + __slots__ = ('defaultAllow', 'expiredBans', 'bans', 'ignores', 'silences', + 'exceptions', 'capabilities', 'lobotomized') defaultOff = ('op', 'halfop', 'voice', 'protected') def __init__(self, bans=None, silences=None, exceptions=None, ignores=None, capabilities=None, lobotomized=False, defaultAllow=True): @@ -491,10 +497,12 @@ class IrcChannel(object): class Creator(object): + __slots__ = () def badCommand(self, command, rest, lineno): raise ValueError('Invalid command on line %s: %s' % (lineno, command)) class IrcUserCreator(Creator): + __slots__ = ('users') u = None def __init__(self, users): if self.u is None: @@ -563,6 +571,7 @@ class IrcUserCreator(Creator): IrcUserCreator.u = None class IrcChannelCreator(Creator): + __slots__ = ('c', 'channels', 'hadChannel') name = None def __init__(self, channels): self.c = IrcChannel() @@ -611,6 +620,8 @@ class DuplicateHostmask(ValueError): class UsersDictionary(utils.IterableMap): """A simple serialized-to-file User Database.""" + __slots__ = ('noFlush', 'filename', 'users', '_nameCache', + '_hostmaskCache') def __init__(self): self.noFlush = False self.filename = None @@ -821,6 +832,7 @@ class UsersDictionary(utils.IterableMap): class ChannelsDictionary(utils.IterableMap): + __slots__ = ('noFlush', 'filename', 'channels') def __init__(self): self.noFlush = False self.filename = None @@ -897,6 +909,7 @@ class ChannelsDictionary(utils.IterableMap): class IgnoresDB(object): + __slots__ = ('filename', 'hostmasks') def __init__(self): self.filename = None self.hostmasks = {} @@ -1145,9 +1158,11 @@ def checkCapabilities(hostmask, capabilities, requireAll=False): ### class SpaceSeparatedListOfCapabilities(registry.SpaceSeparatedListOfStrings): + __slots__ = () List = CapabilitySet class DefaultCapabilities(SpaceSeparatedListOfCapabilities): + __slots__ = () # We use a keyword argument trick here to prevent eval'ing of code that # changes allowDefaultOwner from affecting this. It's not perfect, but # it's still an improvement, raising the bar for potential crackers. diff --git a/src/registry.py b/src/registry.py index 56d9cb7a6..8c74dd56f 100644 --- a/src/registry.py +++ b/src/registry.py @@ -191,6 +191,8 @@ def join(names): class Group(object): """A group; it doesn't hold a value unless handled by a subclass.""" + __slots__ = ('_help', '_name', '_added', '_children', '_lastModified', + '_private', '_supplyDefault', '_orderAlphabetically', '_wasSet') def __init__(self, help='', supplyDefault=False, orderAlphabetically=True, private=False): self._help = utils.str.normalizeWhitespace(help) @@ -201,7 +203,6 @@ class Group(object): self._private = private self._supplyDefault = supplyDefault self._orderAlphabetically = orderAlphabetically - OriginalClass = self.__class__ self._wasSet = True def __call__(self): @@ -326,6 +327,8 @@ class _NoValueGiven: class Value(Group): """Invalid registry value. If you're getting this message, report it, because we forgot to put a proper help string here.""" + __slots__ = ('__parent', '_default', '_showDefault', '_help', '_callbacks', + 'value', 'channelValue', '_opSettable') def __init__(self, default, help, setDefault=True, showDefault=True, **kwargs): self.__parent = super(Value, self) @@ -421,6 +424,7 @@ class Value(Group): class Boolean(Value): """Value must be either True or False (or On or Off).""" + __slots__ = () errormsg = _('Value must be either True or False (or On or Off), not %r.') def set(self, s): try: @@ -437,6 +441,7 @@ class Boolean(Value): class Integer(Value): """Value must be an integer.""" + __slots__ = () errormsg = _('Value must be an integer, not %r.') def set(self, s): try: @@ -446,6 +451,7 @@ class Integer(Value): class NonNegativeInteger(Integer): """Value must be a non-negative integer.""" + __slots__ = () errormsg = _('Value must be a non-negative integer, not %r.') def setValue(self, v): if v < 0: @@ -454,6 +460,7 @@ class NonNegativeInteger(Integer): class PositiveInteger(NonNegativeInteger): """Value must be positive (non-zero) integer.""" + __slots__ = () errormsg = _('Value must be positive (non-zero) integer, not %r.') def setValue(self, v): if not v: @@ -462,6 +469,7 @@ class PositiveInteger(NonNegativeInteger): class Float(Value): """Value must be a floating-point number.""" + __slots__ = () errormsg = _('Value must be a floating-point number, not %r.') def set(self, s): try: @@ -477,6 +485,7 @@ class Float(Value): class PositiveFloat(Float): """Value must be a floating-point number greater than zero.""" + __slots__ = () errormsg = _('Value must be a floating-point number greater than zero, ' 'not %r.') def setValue(self, v): @@ -487,6 +496,7 @@ class PositiveFloat(Float): class Probability(Float): """Value must be a floating point number in the range [0, 1].""" + __slots__ = ('__parent',) errormsg = _('Value must be a floating point number in the range [0, 1], ' 'not %r.') def __init__(self, *args, **kwargs): @@ -501,6 +511,7 @@ class Probability(Float): class String(Value): """Value is not a valid Python string.""" + __slots__ = () errormsg = _('Value should be a valid Python string, not %r.') def set(self, s): v = s @@ -527,6 +538,8 @@ class String(Value): return s class OnlySomeStrings(String): + __slots__ = ('__parent', '__dict__') # unfortunately, __dict__ is needed + # to set __doc__. validStrings = () def __init__(self, *args, **kwargs): assert self.validStrings, 'There must be some valid strings. ' \ @@ -559,6 +572,7 @@ class OnlySomeStrings(String): self.error(v) class NormalizedString(String): + __slots__ = ('__parent') def __init__(self, default, *args, **kwargs): default = self.normalize(default) self.__parent = super(NormalizedString, self) @@ -591,6 +605,7 @@ class NormalizedString(String): return ret class StringSurroundedBySpaces(String): + __slots__ = () def setValue(self, v): if v and v.lstrip() == v: v= ' ' + v @@ -599,6 +614,7 @@ class StringSurroundedBySpaces(String): super(StringSurroundedBySpaces, self).setValue(v) class StringWithSpaceOnRight(String): + __slots__ = () def setValue(self, v): if v and v.rstrip() == v: v += ' ' @@ -606,6 +622,7 @@ class StringWithSpaceOnRight(String): class Regexp(Value): """Value must be a valid regular expression.""" + __slots__ = ('sr', 'value', '__parent') errormsg = _('Value must be a valid regular expression, not %r.') def __init__(self, *args, **kwargs): kwargs['setDefault'] = False @@ -645,6 +662,7 @@ class Regexp(Value): return self.sr class SeparatedListOf(Value): + __slots__ = () List = list Value = Value sorted = False @@ -681,23 +699,28 @@ class SeparatedListOf(Value): return ' ' class SpaceSeparatedListOf(SeparatedListOf): + __slots__ = () def splitter(self, s): return s.split() joiner = ' '.join class SpaceSeparatedListOfStrings(SpaceSeparatedListOf): + __slots__ = () Value = String class SpaceSeparatedSetOfStrings(SpaceSeparatedListOfStrings): + __slots__ = () List = set class CommaSeparatedListOfStrings(SeparatedListOf): + __slots__ = () Value = String def splitter(self, s): return re.split(r'\s*,\s*', s) joiner = ', '.join class CommaSeparatedSetOfStrings(SeparatedListOf): + __slots__ = () List = set Value = String def splitter(self, s): @@ -705,6 +728,7 @@ class CommaSeparatedSetOfStrings(SeparatedListOf): joiner = ', '.join class TemplatedString(String): + __slots__ = () requiredTemplates = [] def __init__(self, *args, **kwargs): assert self.requiredTemplates, \ @@ -721,6 +745,7 @@ class TemplatedString(String): self.error(v) class Json(String): + __slots__ = () # Json-serializable data def set(self, v): self.setValue(json.loads(v))