Update handle_error to be more useful
Now, local variables from the innermost frame (i.e. where the error was raised) are printed with the traceback, and the latter includes all of the call stack (instead of just up to the innermost error handler).
This commit is contained in:
parent
a2257ce692
commit
d602a33efd
@ -1,7 +1,9 @@
|
|||||||
import fnmatch
|
import fnmatch
|
||||||
import socket
|
import socket
|
||||||
import traceback
|
import traceback
|
||||||
|
import threading
|
||||||
import types
|
import types
|
||||||
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from oyoyo.client import IRCClient
|
from oyoyo.client import IRCClient
|
||||||
@ -20,6 +22,14 @@ HOOKS = defaultdict(list)
|
|||||||
|
|
||||||
# Error handler decorators
|
# Error handler decorators
|
||||||
|
|
||||||
|
_do_nothing = lambda: None
|
||||||
|
|
||||||
|
class _local(threading.local):
|
||||||
|
level = 0
|
||||||
|
frame_locals = None
|
||||||
|
|
||||||
|
_local = _local()
|
||||||
|
|
||||||
class handle_error:
|
class handle_error:
|
||||||
|
|
||||||
def __new__(cls, func):
|
def __new__(cls, func):
|
||||||
@ -36,25 +46,43 @@ class handle_error:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
|
fn = _do_nothing
|
||||||
|
_local.level += 1
|
||||||
try:
|
try:
|
||||||
return self.func(*args, **kwargs)
|
return self.func(*args, **kwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc() # no matter what, we want it to print
|
if _local.frame_locals is None:
|
||||||
if kwargs.get("cli"): # client
|
exc_type, exc_value, tb = sys.exc_info()
|
||||||
cli = kwargs["cli"]
|
while tb.tb_next is not None:
|
||||||
else:
|
tb = tb.tb_next
|
||||||
for cli in args:
|
_local.frame_locals = tb.tb_frame.f_locals
|
||||||
if isinstance(cli, IRCClient):
|
|
||||||
break
|
if _local.level > 1:
|
||||||
else:
|
raise # the outermost caller should handle this
|
||||||
cli = None
|
|
||||||
|
fn = lambda: errlog("\n" + data)
|
||||||
|
data = traceback.format_exc()
|
||||||
|
cli = None
|
||||||
|
variables = ["\nLocal variables from innermost frame:\n"]
|
||||||
|
for name, value in _local.frame_locals.items():
|
||||||
|
if isinstance(value, IRCClient):
|
||||||
|
cli = value
|
||||||
|
|
||||||
|
variables.append("{0} = {1!r}".format(name, value))
|
||||||
|
|
||||||
|
data += "\n".join(variables)
|
||||||
|
|
||||||
if cli is not None:
|
if cli is not None:
|
||||||
if not botconfig.PASTEBIN_ERRORS or botconfig.CHANNEL != botconfig.DEV_CHANNEL:
|
if not botconfig.PASTEBIN_ERRORS or botconfig.CHANNEL != botconfig.DEV_CHANNEL:
|
||||||
cli.msg(botconfig.CHANNEL, messages["error_log"])
|
cli.msg(botconfig.CHANNEL, messages["error_log"])
|
||||||
errlog(traceback.format_exc())
|
|
||||||
if botconfig.PASTEBIN_ERRORS and botconfig.DEV_CHANNEL:
|
if botconfig.PASTEBIN_ERRORS and botconfig.DEV_CHANNEL:
|
||||||
pastebin_tb(cli, messages["error_log"], traceback.format_exc())
|
pastebin_tb(cli, messages["error_log"], data)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
fn()
|
||||||
|
_local.level -= 1
|
||||||
|
if not _local.level: # outermost caller; we're done here
|
||||||
|
_local.frame_locals = None
|
||||||
|
|
||||||
class cmd:
|
class cmd:
|
||||||
def __init__(self, *cmds, raw_nick=False, flag=None, owner_only=False,
|
def __init__(self, *cmds, raw_nick=False, flag=None, owner_only=False,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user