mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-01 01:09:22 +01:00
Split Irc.reply() into _reply() to make 'networks.remote' actually thread-safe
Previously, the Irc.reply_lock check was in the reply() function itself: replacing it with another function checking for the same lock would delay execution, but then run the wrong reply() code if another module used irc.reply() while 'remote' was executing.
This commit is contained in:
parent
40fa4f71bc
commit
9d9b01839c
49
classes.py
49
classes.py
@ -62,7 +62,7 @@ class Irc(utils.DeprecatedAttributesObject):
|
|||||||
|
|
||||||
self.connected = threading.Event()
|
self.connected = threading.Event()
|
||||||
self.aborted = threading.Event()
|
self.aborted = threading.Event()
|
||||||
self.reply_lock = threading.Lock()
|
self.reply_lock = threading.RLock()
|
||||||
|
|
||||||
self.pingTimer = None
|
self.pingTimer = None
|
||||||
|
|
||||||
@ -559,27 +559,38 @@ class Irc(utils.DeprecatedAttributesObject):
|
|||||||
# replies across relay.
|
# replies across relay.
|
||||||
self.callHooks([source, cmd, {'target': target, 'text': text}])
|
self.callHooks([source, cmd, {'target': target, 'text': text}])
|
||||||
|
|
||||||
def reply(self, text, notice=None, source=None, private=None, force_privmsg_in_private=False,
|
def _reply(self, text, notice=None, source=None, private=None, force_privmsg_in_private=False,
|
||||||
loopback=True):
|
loopback=True):
|
||||||
"""Replies to the last caller in the right context (channel or PM)."""
|
"""
|
||||||
|
Core of the reply() function - replies to the last caller in the right context
|
||||||
|
(channel or PM).
|
||||||
|
"""
|
||||||
|
if private is None:
|
||||||
|
# Allow using private replies as the default, if no explicit setting was given.
|
||||||
|
private = conf.conf['bot'].get("prefer_private_replies")
|
||||||
|
|
||||||
|
# Private reply is enabled, or the caller was originally a PM
|
||||||
|
if private or (self.called_in in self.users):
|
||||||
|
if not force_privmsg_in_private:
|
||||||
|
# For private replies, the default is to override the notice=True/False argument,
|
||||||
|
# and send replies as notices regardless. This is standard behaviour for most
|
||||||
|
# IRC services, but can be disabled if force_privmsg_in_private is given.
|
||||||
|
notice = True
|
||||||
|
target = self.called_by
|
||||||
|
else:
|
||||||
|
target = self.called_in
|
||||||
|
|
||||||
|
self.msg(target, text, notice=notice, source=source, loopback=loopback)
|
||||||
|
|
||||||
|
def reply(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Replies to the last caller in the right context (channel or PM).
|
||||||
|
|
||||||
|
This function wraps around _reply() and can be monkey-patched in a thread-safe manner
|
||||||
|
to temporarily redirect plugin output to another target.
|
||||||
|
"""
|
||||||
with self.reply_lock:
|
with self.reply_lock:
|
||||||
if private is None:
|
self._reply(*args, **kwargs)
|
||||||
# Allow using private replies as the default, if no explicit setting was given.
|
|
||||||
private = conf.conf['bot'].get("prefer_private_replies")
|
|
||||||
|
|
||||||
# Private reply is enabled, or the caller was originally a PM
|
|
||||||
if private or (self.called_in in self.users):
|
|
||||||
if not force_privmsg_in_private:
|
|
||||||
# For private replies, the default is to override the notice=True/False argument,
|
|
||||||
# and send replies as notices regardless. This is standard behaviour for most
|
|
||||||
# IRC services, but can be disabled if force_privmsg_in_private is given.
|
|
||||||
notice = True
|
|
||||||
target = self.called_by
|
|
||||||
else:
|
|
||||||
target = self.called_in
|
|
||||||
|
|
||||||
self.msg(target, text, notice=notice, source=source, loopback=loopback)
|
|
||||||
|
|
||||||
def error(self, text, **kwargs):
|
def error(self, text, **kwargs):
|
||||||
"""Replies with an error to the last caller in the right context (channel or PM)."""
|
"""Replies with an error to the last caller in the right context (channel or PM)."""
|
||||||
|
@ -103,19 +103,19 @@ def remote(irc, source, args):
|
|||||||
del kwargs['source']
|
del kwargs['source']
|
||||||
irc.reply(text, source=irc.pseudoclient.uid, **kwargs)
|
irc.reply(text, source=irc.pseudoclient.uid, **kwargs)
|
||||||
|
|
||||||
old_reply = remoteirc.reply
|
old_reply = remoteirc._reply
|
||||||
|
|
||||||
with remoteirc.reply_lock:
|
with remoteirc.reply_lock:
|
||||||
try: # Remotely call the command (use the PyLink client as a dummy user).
|
try: # Remotely call the command (use the PyLink client as a dummy user).
|
||||||
# Override the remote irc.reply() to send replies HERE.
|
# Override the remote irc.reply() to send replies HERE.
|
||||||
log.debug('(%s) networks.remote: overriding reply() of IRC object %s', irc.name, netname)
|
log.debug('(%s) networks.remote: overriding reply() of IRC object %s', irc.name, netname)
|
||||||
remoteirc.reply = types.MethodType(_remote_reply, remoteirc)
|
remoteirc._reply = types.MethodType(_remote_reply, remoteirc)
|
||||||
world.services[args.service].call_cmd(remoteirc, remoteirc.pseudoclient.uid,
|
world.services[args.service].call_cmd(remoteirc, remoteirc.pseudoclient.uid,
|
||||||
' '.join(args.command))
|
' '.join(args.command))
|
||||||
finally:
|
finally:
|
||||||
# Restore the original remoteirc.reply()
|
# Restore the original remoteirc.reply()
|
||||||
log.debug('(%s) networks.remote: restoring reply() of IRC object %s', irc.name, netname)
|
log.debug('(%s) networks.remote: restoring reply() of IRC object %s', irc.name, netname)
|
||||||
remoteirc.reply = old_reply
|
remoteirc._reply = old_reply
|
||||||
# Remove the identification override after we finish.
|
# Remove the identification override after we finish.
|
||||||
remoteirc.pseudoclient.account = ''
|
remoteirc.pseudoclient.account = ''
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user