Fix bypassed cache invalidation checks, causing '@config reload' to be partially ignored

`registry.Value.__call__()` is a wrapper around access to
`registry.Value.value`, that checks if the value was set before the latest
call to `registry.open_registry`; and updates the `value` if needed.

When accessing `registry.Value.value` directly, this cache can't be
invalidated, causing the old value to still be used, until the next call
to `registry.Value.__call__()`.
This commit is contained in:
Valentin Lorentz 2021-12-03 16:51:15 +01:00
parent c0d033ff84
commit baa8cda814
9 changed files with 40 additions and 19 deletions

View File

@ -85,7 +85,7 @@ class String256(registry.String):
return s * (1024//len(s))
def __str__(self):
return self.value
return self()
conf.registerGlobalValue(BadWords, 'nastyChars',
String256('!@#&', _("""Determines what characters will replace bad words; a

View File

@ -50,6 +50,7 @@ class Smileys(registry.Value):
self.setValue(L)
def setValue(self, v):
self._lastModified = registry.monotonic_time()
self.s = ' '.join(v)
self.value = re.compile('|'.join(map(re.escape, v)))

View File

@ -499,12 +499,12 @@ class Config(callbacks.Plugin):
netgroup = group.get(':' + network.network)
changroup = netgroup.get(channel)
checkCanSetValue(irc, msg, changroup)
changroup._setValue(netgroup.value, inherited=True)
changroup._setValue(netgroup(), inherited=True)
# reset group.#channel
changroup = group.get(channel)
checkCanSetValue(irc, msg, changroup)
changroup._setValue(group.value, inherited=True)
changroup._setValue(group(), inherited=True)
irc.replySuccess()
channel = wrap(channel, [
@ -523,7 +523,7 @@ class Config(callbacks.Plugin):
# reset group.#channel
changroup = group.get(':' + network.network)
checkCanSetValue(irc, msg, changroup)
changroup._setValue(group.value, inherited=True)
changroup._setValue(group(), inherited=True)
irc.replySuccess()
network = wrap(network, ['networkIrc', 'settableConfigVar'])

View File

@ -107,6 +107,24 @@ class ConfigTestCase(ChannelPluginTestCase):
r'supybot.reply.whenAddressedBy.strings.#test and '
r'supybot.reply.whenAddressedBy.strings.\:test.#test')
def testReload(self):
old_password = 'pjfoizjoifjfoii_old'
new_password = 'pjfoizjoifjfoii_new'
with conf.supybot.networks.test.password.context(old_password):
self.assertResponse('config conf.supybot.networks.test.password',
old_password)
filename = conf.supybot.directories.conf.dirize('Config_testReload.conf')
registry.close(conf.supybot, filename)
content = open(filename).read()
assert old_password in content
open(filename, 'wt').write(content.replace(old_password,
new_password))
registry.open_registry(filename)
self.assertResponse('config conf.supybot.networks.test.password',
new_password)
def testDefault(self):
self.assertNotError('config default '
'supybot.replies.genericNoCapability')

View File

@ -43,7 +43,7 @@ class IrcLogLevel(log.ValidLogLevel):
minimumLevel = logging.INFO
def setValue(self, v):
log.ValidLogLevel.setValue(self, v)
_ircHandler.setLevel(self.value)
_ircHandler.setLevel(self())
class ValidChannelOrNick(registry.String):
"""Value must be a valid channel or a valid nick."""

View File

@ -791,7 +791,7 @@ class ValidQuotes(registry.Value):
super(ValidQuotes, self).setValue(v)
def __str__(self):
return str(self.value)
return str(self())
registerChannelValue(supybot.commands, 'quotes',
ValidQuotes('"', _("""Determines what characters are valid for quoting
@ -1030,7 +1030,7 @@ class Databases(registry.SpaceSeparatedListOfStrings):
return v
def serialize(self):
return ' '.join(self.value)
return ' '.join(self())
registerGlobalValue(supybot, 'databases',
Databases([], _("""Determines what databases are available for use. If this
@ -1466,7 +1466,7 @@ class SocketTimeout(registry.PositiveInteger):
if v < supybot.drivers.poll() or v < 1:
self.error()
registry.PositiveInteger.setValue(self, v)
socket.setdefaulttimeout(self.value)
socket.setdefaulttimeout(self())
registerGlobalValue(supybot, 'defaultSocketTimeout',
SocketTimeout(10, _("""Determines what the default timeout for socket

View File

@ -77,12 +77,12 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
self.writeCheckTime = None
self.nextReconnectTime = None
self.resetDelay()
if self.networkGroup.get('ssl').value and 'ssl' not in globals():
if self.networkGroup.get('ssl')() and 'ssl' not in globals():
drivers.log.error('The Socket driver can not connect to SSL '
'servers for your Python version.')
self.ssl = False
else:
self.ssl = self.networkGroup.get('ssl').value
self.ssl = self.networkGroup.get('ssl')()
self.connect()
def getDelay(self):

View File

@ -1325,11 +1325,11 @@ class DefaultCapabilities(SpaceSeparatedListOfCapabilities):
# it's still an improvement, raising the bar for potential crackers.
def setValue(self, v, allowDefaultOwner=conf.allowDefaultOwner):
registry.SpaceSeparatedListOfStrings.setValue(self, v)
if '-owner' not in self.value and not allowDefaultOwner:
if '-owner' not in self() and not allowDefaultOwner:
print('*** You must run supybot with the --allow-default-owner')
print('*** option in order to allow a default capability of owner.')
print('*** Don\'t do that, it\'s dumb.')
self.value.add('-owner')
self().add('-owner')
conf.registerGlobalValue(conf.supybot, 'capabilities',
DefaultCapabilities([

View File

@ -519,7 +519,7 @@ class Value(Group):
context."""
class Context:
def __enter__(self2):
self2._old_value = self.value
self2._old_value = self()
self.setValue(value)
def __exit__(self2, exc_type, exc_value, traceback):
self.setValue(self2._old_value)
@ -559,7 +559,7 @@ class Boolean(Value):
v = utils.str.toBool(s)
except ValueError:
if s.strip().lower() == 'toggle':
v = not self.value
v = not self()
else:
self.error(s)
self.setValue(v)
@ -660,7 +660,7 @@ class String(Value):
return any([x not in self._printable for x in s]) and s.strip() != s
def __str__(self):
s = self.value
s = self()
if self._needsQuoting(s):
s = repr(s)
return s
@ -798,16 +798,18 @@ class Regexp(Value):
super().setValue(v)
def __call__(self):
if self.value is None:
value = super().__call__()
if value is None:
return None
else:
return self.value[1]
return value[1]
def __str__(self):
if self.value is None:
value = super().__call__()
if value is None:
return ''
else:
return self.value[0]
return value[0]
class SeparatedListOf(Value):
__slots__ = ()