Properly fix the error handler
This commit is contained in:
parent
154589a748
commit
b28d4bf6e3
@ -1,10 +1,6 @@
|
||||
import traceback
|
||||
import argparse
|
||||
import datetime
|
||||
import socket
|
||||
import time
|
||||
import sys
|
||||
import io
|
||||
|
||||
import botconfig
|
||||
import src.settings as var
|
||||
@ -117,78 +113,3 @@ def stream(output, level="normal"):
|
||||
stream_handler(output)
|
||||
elif level == "warning":
|
||||
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 socket
|
||||
from collections import defaultdict
|
||||
|
||||
from oyoyo.client import IRCClient
|
||||
from oyoyo.parse import parse_nick
|
||||
|
||||
import botconfig
|
||||
@ -9,10 +12,53 @@ from src.utilities import *
|
||||
from src import logger
|
||||
|
||||
adminlog = logger("audit.log")
|
||||
errlog = logger("errors.log")
|
||||
|
||||
COMMANDS = 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:
|
||||
def __init__(self, *cmds, raw_nick=False, admin_only=False, owner_only=False,
|
||||
chan=True, pm=False, playing=False, silenced=False, phases=(), roles=()):
|
||||
@ -49,6 +95,7 @@ class cmd:
|
||||
self.__doc__ = self.func.__doc__
|
||||
return self
|
||||
|
||||
@handle_error
|
||||
def caller(self, *args):
|
||||
largs = list(args)
|
||||
|
||||
@ -190,6 +237,10 @@ class hook:
|
||||
self.__doc__ = self.func.__doc__
|
||||
return self
|
||||
|
||||
@handle_error
|
||||
def caller(self, *args, **kwargs):
|
||||
return self.func(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def unhook(hookid):
|
||||
for each in list(HOOKS):
|
||||
|
@ -14,8 +14,6 @@ from src import decorators, logger, wolfgame
|
||||
log = logger("errors.log")
|
||||
alog = logger(None)
|
||||
|
||||
sys.stderr.target_logger = log
|
||||
|
||||
hook = decorators.hook
|
||||
|
||||
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]
|
||||
|
||||
for fn in decorators.COMMANDS[""]:
|
||||
try:
|
||||
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()):
|
||||
@ -67,12 +60,7 @@ def on_privmsg(cli, rawnick, chan, msg, notice = False):
|
||||
continue
|
||||
if not h or h[0] == " ":
|
||||
for fn in decorators.COMMANDS.get(x, []):
|
||||
try:
|
||||
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):
|
||||
@ -81,12 +69,7 @@ def unhandled(cli, prefix, cmd, *args):
|
||||
for i,arg in enumerate(largs):
|
||||
if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
|
||||
for fn in decorators.HOOKS.get(cmd, []):
|
||||
try:
|
||||
fn.func(cli, prefix, *largs)
|
||||
except Exception:
|
||||
sys.stderr.write(traceback.format_exc())
|
||||
if botconfig.DEBUG_MODE:
|
||||
raise
|
||||
fn.caller(cli, prefix, *largs)
|
||||
|
||||
def connect_callback(cli):
|
||||
@hook("endofmotd", hookid=294)
|
||||
@ -172,9 +155,6 @@ def connect_callback(cli):
|
||||
# capability but now claims otherwise.
|
||||
alog("Server refused capabilities: {0}".format(" ".join(caps)))
|
||||
|
||||
if sys.stderr.cli is None:
|
||||
sys.stderr.cli = cli # first connection
|
||||
|
||||
if botconfig.SASL_AUTHENTICATION:
|
||||
@hook("authenticate")
|
||||
def auth_plus(cli, something, plus):
|
||||
|
@ -60,6 +60,7 @@ is_owner = var.is_owner
|
||||
|
||||
cmd = decorators.cmd
|
||||
hook = decorators.hook
|
||||
handle_error = decorators.handle_error
|
||||
COMMANDS = decorators.COMMANDS
|
||||
|
||||
# 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(hostmask)
|
||||
|
||||
@handle_error
|
||||
def join_timer_handler(cli):
|
||||
with var.WARNING_LOCK:
|
||||
var.PINGING_IFS = True
|
||||
@ -1267,6 +1269,7 @@ def join_player(cli, player, chan, who = None, forced = False):
|
||||
|
||||
return True
|
||||
|
||||
@handle_error
|
||||
def kill_join(cli, chan):
|
||||
pl = var.list_players()
|
||||
pl.sort(key=lambda x: x.lower())
|
||||
@ -1995,6 +1998,7 @@ def stats(cli, nick, chan, rest):
|
||||
var.PHASE)
|
||||
reply(cli, nick, chan, stats_mssg)
|
||||
|
||||
@handle_error
|
||||
def hurry_up(cli, gameid, change):
|
||||
if var.PHASE != "day": return
|
||||
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"])
|
||||
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):
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
@handle_error
|
||||
def reaper(cli, gameid):
|
||||
# check to see if idlers need to be killed.
|
||||
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))
|
||||
|
||||
@handle_error
|
||||
def return_to_village(cli, chan, nick, show_message):
|
||||
with var.GRAVEYARD_LOCK:
|
||||
if nick in var.DISCONNECTED.keys():
|
||||
@ -3849,6 +3855,7 @@ def begin_day(cli):
|
||||
event = Event("begin_day", {})
|
||||
event.dispatch(cli, var)
|
||||
|
||||
@handle_error
|
||||
def night_warn(cli, gameid):
|
||||
if gameid != var.NIGHT_ID:
|
||||
return
|
||||
@ -7459,7 +7466,7 @@ def cgamemode(cli, arg):
|
||||
else:
|
||||
cli.msg(chan, "Mode \u0002{0}\u0002 not found.".format(modeargs[0]))
|
||||
|
||||
|
||||
@handle_error
|
||||
def expire_start_votes(cli, chan):
|
||||
# Should never happen as the timer is removed on game start, but just to be safe
|
||||
if var.PHASE != 'join':
|
||||
|
Loading…
Reference in New Issue
Block a user