Properly fix the error handler
This commit is contained in:
parent
154589a748
commit
b28d4bf6e3
@ -1,10 +1,6 @@
|
|||||||
import traceback
|
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
import socket
|
|
||||||
import time
|
import time
|
||||||
import sys
|
|
||||||
import io
|
|
||||||
|
|
||||||
import botconfig
|
import botconfig
|
||||||
import src.settings as var
|
import src.settings as var
|
||||||
@ -117,78 +113,3 @@ def stream(output, level="normal"):
|
|||||||
stream_handler(output)
|
stream_handler(output)
|
||||||
elif level == "warning":
|
elif level == "warning":
|
||||||
stream_handler(output)
|
stream_handler(output)
|
||||||
|
|
||||||
# Error handler
|
|
||||||
|
|
||||||
buffer = io.BufferedWriter(io.FileIO(file=sys.stderr.fileno(), mode="wb", closefd=False))
|
|
||||||
|
|
||||||
class ErrorHandler(io.TextIOWrapper):
|
|
||||||
"""Handle tracebacks sent to sys.stderr."""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.cli = None
|
|
||||||
self.target_logger = None
|
|
||||||
self.data = None
|
|
||||||
|
|
||||||
def write(self, data):
|
|
||||||
if self.closed:
|
|
||||||
raise ValueError("write to closed file")
|
|
||||||
if not isinstance(data, str):
|
|
||||||
raise ValueError("can't write %s to text stream" % data.__class__.__name__)
|
|
||||||
length = len(data)
|
|
||||||
b = data.encode("utf-8", "replace")
|
|
||||||
self.buffer.write(b)
|
|
||||||
self.data = data
|
|
||||||
self.flush()
|
|
||||||
return length
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
# Probably a syntax error on startup, so these aren't defined yet
|
|
||||||
# If we do nothing, the error magically is printed to the console
|
|
||||||
if self.cli is None or self.target_logger is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.buffer.flush()
|
|
||||||
|
|
||||||
if self.data is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
exc = self.data.rstrip().splitlines()[-1].partition(":")[0]
|
|
||||||
|
|
||||||
import builtins
|
|
||||||
|
|
||||||
if "." in exc:
|
|
||||||
import importlib
|
|
||||||
module, dot, name = exc.rpartition(".")
|
|
||||||
try:
|
|
||||||
module = importlib.import_module(module)
|
|
||||||
except ImportError:
|
|
||||||
exc = Exception
|
|
||||||
else:
|
|
||||||
exc = getattr(module, name.strip())
|
|
||||||
|
|
||||||
elif hasattr(builtins, exc):
|
|
||||||
exc = getattr(builtins, exc)
|
|
||||||
|
|
||||||
if not isinstance(exc, type) or not issubclass(exc, Exception):
|
|
||||||
self.data = None
|
|
||||||
return # not an actual exception
|
|
||||||
|
|
||||||
msg = "An error has occurred and has been logged."
|
|
||||||
if not botconfig.PASTEBIN_ERRORS or botconfig.CHANNEL != botconfig.DEV_CHANNEL:
|
|
||||||
self.cli.msg(botconfig.CHANNEL, msg)
|
|
||||||
if botconfig.PASTEBIN_ERRORS and botconfig.DEV_CHANNEL:
|
|
||||||
try:
|
|
||||||
with socket.socket() as sock:
|
|
||||||
sock.connect(("termbin.com", 9999))
|
|
||||||
sock.send(b"".join(s.encode("utf-8", "replace") for s in self.data) + b"\n")
|
|
||||||
url = sock.recv(1024).decode("utf-8")
|
|
||||||
except socket.error:
|
|
||||||
self.target_logger(self.data, display=False)
|
|
||||||
else:
|
|
||||||
self.cli.msg(botconfig.DEV_CHANNEL, " ".join((msg, url)))
|
|
||||||
self.data = None
|
|
||||||
|
|
||||||
sys.stderr = ErrorHandler(buffer=buffer, encoding=sys.stderr.encoding,
|
|
||||||
errors=sys.stderr.errors, line_buffering=sys.stderr.line_buffering)
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
import traceback
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import socket
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from oyoyo.client import IRCClient
|
||||||
from oyoyo.parse import parse_nick
|
from oyoyo.parse import parse_nick
|
||||||
|
|
||||||
import botconfig
|
import botconfig
|
||||||
@ -9,10 +12,53 @@ from src.utilities import *
|
|||||||
from src import logger
|
from src import logger
|
||||||
|
|
||||||
adminlog = logger("audit.log")
|
adminlog = logger("audit.log")
|
||||||
|
errlog = logger("errors.log")
|
||||||
|
|
||||||
COMMANDS = defaultdict(list)
|
COMMANDS = defaultdict(list)
|
||||||
HOOKS = defaultdict(list)
|
HOOKS = defaultdict(list)
|
||||||
|
|
||||||
|
# Error handler decorators
|
||||||
|
|
||||||
|
class handle_error:
|
||||||
|
def __init__(self, func):
|
||||||
|
self.func = func
|
||||||
|
self.instance = None
|
||||||
|
self.owner = object
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
self.instance = instance
|
||||||
|
self.owner = owner
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
self.func.__get__(self.instance, self.owner)(*args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc() # no matter what, we want it to print
|
||||||
|
if kwargs.get("cli"): # client
|
||||||
|
cli = kwargs["cli"]
|
||||||
|
else:
|
||||||
|
for cli in args:
|
||||||
|
if isinstance(cli, IRCClient):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
cli = None
|
||||||
|
|
||||||
|
if cli is not None:
|
||||||
|
msg = "An error has occurred and has been logged."
|
||||||
|
if not botconfig.PASTEBIN_ERRORS or botconfig.CHANNEL != botconfig.DEV_CHANNEL:
|
||||||
|
cli.msg(botconfig.CHANNEL, msg)
|
||||||
|
if botconfig.PASTEBIN_ERRORS and botconfig.DEV_CHANNEL:
|
||||||
|
try:
|
||||||
|
with socket.socket() as sock:
|
||||||
|
sock.connect(("termbin.com", 9999))
|
||||||
|
sock.send(traceback.format_exc().encode("utf-8", "replace") + b"\n")
|
||||||
|
url = sock.recv(1024).decode("utf-8")
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
cli.msg(botconfig.DEV_CHANNEL, " ".join((msg, url)))
|
||||||
|
|
||||||
class cmd:
|
class cmd:
|
||||||
def __init__(self, *cmds, raw_nick=False, admin_only=False, owner_only=False,
|
def __init__(self, *cmds, raw_nick=False, admin_only=False, owner_only=False,
|
||||||
chan=True, pm=False, playing=False, silenced=False, phases=(), roles=()):
|
chan=True, pm=False, playing=False, silenced=False, phases=(), roles=()):
|
||||||
@ -49,6 +95,7 @@ class cmd:
|
|||||||
self.__doc__ = self.func.__doc__
|
self.__doc__ = self.func.__doc__
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def caller(self, *args):
|
def caller(self, *args):
|
||||||
largs = list(args)
|
largs = list(args)
|
||||||
|
|
||||||
@ -190,6 +237,10 @@ class hook:
|
|||||||
self.__doc__ = self.func.__doc__
|
self.__doc__ = self.func.__doc__
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@handle_error
|
||||||
|
def caller(self, *args, **kwargs):
|
||||||
|
return self.func(*args, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def unhook(hookid):
|
def unhook(hookid):
|
||||||
for each in list(HOOKS):
|
for each in list(HOOKS):
|
||||||
|
@ -14,8 +14,6 @@ from src import decorators, logger, wolfgame
|
|||||||
log = logger("errors.log")
|
log = logger("errors.log")
|
||||||
alog = logger(None)
|
alog = logger(None)
|
||||||
|
|
||||||
sys.stderr.target_logger = log
|
|
||||||
|
|
||||||
hook = decorators.hook
|
hook = decorators.hook
|
||||||
|
|
||||||
def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
||||||
@ -48,12 +46,7 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
|||||||
chan = parse_nick(rawnick)[0]
|
chan = parse_nick(rawnick)[0]
|
||||||
|
|
||||||
for fn in decorators.COMMANDS[""]:
|
for fn in decorators.COMMANDS[""]:
|
||||||
try:
|
|
||||||
fn.caller(cli, rawnick, chan, msg)
|
fn.caller(cli, rawnick, chan, msg)
|
||||||
except Exception:
|
|
||||||
sys.stderr.write(traceback.format_exc())
|
|
||||||
if botconfig.DEBUG_MODE:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
for x in list(decorators.COMMANDS.keys()):
|
for x in list(decorators.COMMANDS.keys()):
|
||||||
@ -67,12 +60,7 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
|||||||
continue
|
continue
|
||||||
if not h or h[0] == " ":
|
if not h or h[0] == " ":
|
||||||
for fn in decorators.COMMANDS.get(x, []):
|
for fn in decorators.COMMANDS.get(x, []):
|
||||||
try:
|
|
||||||
fn.caller(cli, rawnick, chan, h.lstrip())
|
fn.caller(cli, rawnick, chan, h.lstrip())
|
||||||
except Exception:
|
|
||||||
sys.stderr.write(traceback.format_exc())
|
|
||||||
if botconfig.DEBUG_MODE:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def unhandled(cli, prefix, cmd, *args):
|
def unhandled(cli, prefix, cmd, *args):
|
||||||
@ -81,12 +69,7 @@ def unhandled(cli, prefix, cmd, *args):
|
|||||||
for i,arg in enumerate(largs):
|
for i,arg in enumerate(largs):
|
||||||
if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
|
if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
|
||||||
for fn in decorators.HOOKS.get(cmd, []):
|
for fn in decorators.HOOKS.get(cmd, []):
|
||||||
try:
|
fn.caller(cli, prefix, *largs)
|
||||||
fn.func(cli, prefix, *largs)
|
|
||||||
except Exception:
|
|
||||||
sys.stderr.write(traceback.format_exc())
|
|
||||||
if botconfig.DEBUG_MODE:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def connect_callback(cli):
|
def connect_callback(cli):
|
||||||
@hook("endofmotd", hookid=294)
|
@hook("endofmotd", hookid=294)
|
||||||
@ -172,9 +155,6 @@ def connect_callback(cli):
|
|||||||
# capability but now claims otherwise.
|
# capability but now claims otherwise.
|
||||||
alog("Server refused capabilities: {0}".format(" ".join(caps)))
|
alog("Server refused capabilities: {0}".format(" ".join(caps)))
|
||||||
|
|
||||||
if sys.stderr.cli is None:
|
|
||||||
sys.stderr.cli = cli # first connection
|
|
||||||
|
|
||||||
if botconfig.SASL_AUTHENTICATION:
|
if botconfig.SASL_AUTHENTICATION:
|
||||||
@hook("authenticate")
|
@hook("authenticate")
|
||||||
def auth_plus(cli, something, plus):
|
def auth_plus(cli, something, plus):
|
||||||
|
@ -60,6 +60,7 @@ is_owner = var.is_owner
|
|||||||
|
|
||||||
cmd = decorators.cmd
|
cmd = decorators.cmd
|
||||||
hook = decorators.hook
|
hook = decorators.hook
|
||||||
|
handle_error = decorators.handle_error
|
||||||
COMMANDS = decorators.COMMANDS
|
COMMANDS = decorators.COMMANDS
|
||||||
|
|
||||||
# Game Logic Begins:
|
# Game Logic Begins:
|
||||||
@ -918,6 +919,7 @@ def toggle_altpinged_status(nick, value, old=None):
|
|||||||
var.PING_IF_NUMS[old].discard(host)
|
var.PING_IF_NUMS[old].discard(host)
|
||||||
var.PING_IF_NUMS[old].discard(hostmask)
|
var.PING_IF_NUMS[old].discard(hostmask)
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def join_timer_handler(cli):
|
def join_timer_handler(cli):
|
||||||
with var.WARNING_LOCK:
|
with var.WARNING_LOCK:
|
||||||
var.PINGING_IFS = True
|
var.PINGING_IFS = True
|
||||||
@ -1267,6 +1269,7 @@ def join_player(cli, player, chan, who = None, forced = False):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def kill_join(cli, chan):
|
def kill_join(cli, chan):
|
||||||
pl = var.list_players()
|
pl = var.list_players()
|
||||||
pl.sort(key=lambda x: x.lower())
|
pl.sort(key=lambda x: x.lower())
|
||||||
@ -1995,6 +1998,7 @@ def stats(cli, nick, chan, rest):
|
|||||||
var.PHASE)
|
var.PHASE)
|
||||||
reply(cli, nick, chan, stats_mssg)
|
reply(cli, nick, chan, stats_mssg)
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def hurry_up(cli, gameid, change):
|
def hurry_up(cli, gameid, change):
|
||||||
if var.PHASE != "day": return
|
if var.PHASE != "day": return
|
||||||
if gameid:
|
if gameid:
|
||||||
@ -2764,6 +2768,7 @@ def chk_win_conditions(lpl, lwolves, lcubs, lrealwolves, lmonsters, ldemoniacs,
|
|||||||
stop_game(cli, winner, additional_winners=event.data["additional_winners"])
|
stop_game(cli, winner, additional_winners=event.data["additional_winners"])
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death_triggers=True, killer_role="", deadlist=[], original="", cmode=[], deadchat=[], ismain=True):
|
def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death_triggers=True, killer_role="", deadlist=[], original="", cmode=[], deadchat=[], ismain=True):
|
||||||
"""
|
"""
|
||||||
Returns: False if one side won.
|
Returns: False if one side won.
|
||||||
@ -3212,7 +3217,7 @@ def del_player(cli, nick, forced_death=False, devoice=True, end_game=True, death
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def reaper(cli, gameid):
|
def reaper(cli, gameid):
|
||||||
# check to see if idlers need to be killed.
|
# check to see if idlers need to be killed.
|
||||||
var.IDLE_WARNED = set()
|
var.IDLE_WARNED = set()
|
||||||
@ -3414,6 +3419,7 @@ def fgoat(cli, nick, chan, rest):
|
|||||||
|
|
||||||
cli.msg(chan, "\u0002{0}\u0002's goat walks by and {1} \u0002{2}\u0002.".format(nick, goatact, togoat))
|
cli.msg(chan, "\u0002{0}\u0002's goat walks by and {1} \u0002{2}\u0002.".format(nick, goatact, togoat))
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def return_to_village(cli, chan, nick, show_message):
|
def return_to_village(cli, chan, nick, show_message):
|
||||||
with var.GRAVEYARD_LOCK:
|
with var.GRAVEYARD_LOCK:
|
||||||
if nick in var.DISCONNECTED.keys():
|
if nick in var.DISCONNECTED.keys():
|
||||||
@ -3849,6 +3855,7 @@ def begin_day(cli):
|
|||||||
event = Event("begin_day", {})
|
event = Event("begin_day", {})
|
||||||
event.dispatch(cli, var)
|
event.dispatch(cli, var)
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def night_warn(cli, gameid):
|
def night_warn(cli, gameid):
|
||||||
if gameid != var.NIGHT_ID:
|
if gameid != var.NIGHT_ID:
|
||||||
return
|
return
|
||||||
@ -7459,7 +7466,7 @@ def cgamemode(cli, arg):
|
|||||||
else:
|
else:
|
||||||
cli.msg(chan, "Mode \u0002{0}\u0002 not found.".format(modeargs[0]))
|
cli.msg(chan, "Mode \u0002{0}\u0002 not found.".format(modeargs[0]))
|
||||||
|
|
||||||
|
@handle_error
|
||||||
def expire_start_votes(cli, chan):
|
def expire_start_votes(cli, chan):
|
||||||
# Should never happen as the timer is removed on game start, but just to be safe
|
# Should never happen as the timer is removed on game start, but just to be safe
|
||||||
if var.PHASE != 'join':
|
if var.PHASE != 'join':
|
||||||
|
Loading…
Reference in New Issue
Block a user