馃悕馃悕馃悕
1import dis
2
3class Errs(type):
4 def __iter__(_class):
5 return iter((k,v) for (k,v) in vars(_class).items() if not k.startswith("_"))
6
7 def __repr__(_class):
8 return str(list(k for (k,v) in _class))
9
10def errs(errs):
11 def _errs(fn):
12 fn.errs = errs
13 return fn
14 return _errs
15
16# stuff above this line i probs move to another file
17
18class _Errs(metaclass=Errs):
19 FOUND_RETURN = "encountered return instruction"
20 IN_DEFINITION = "error in fn definition"
21 IN_EXECUTION = "error in fn execution"
22 NON_DICT_RETURN = "fn dumped a non-dict"
23
24def _modify_to_dump_locals(fn, fn_source, deftime_globals, log):
25 instructions = dis.get_instructions(fn)
26 for instruction in instructions:
27 if instruction.opcode == dis.opmap["RETURN_VALUE"]:
28 return (False, _Errs.FOUND_RETURN)
29 fn_source = fn_source
30 fn_source_modified = f"{fn_source}\n return locals()"
31 sandbox = {}
32 try:
33 exec(fn_source_modified, globals=deftime_globals, locals=sandbox)
34 except KeyboardInterrupt:
35 raise
36 except:
37 log.indented().trace(source=fn)
38 return (False, _Errs.IN_DEFINITION)
39 return (True, sandbox[fn.__name__])
40
41@errs(_Errs)
42def try_dump_locals(fn, fn_source, args, kwargs, deftime_globals, log):
43 (success, fn_or_err) = _modify_to_dump_locals(fn, fn_source, deftime_globals, log)
44 if not success:
45 return (False, fn_or_err)
46 try:
47 res = fn_or_err(*args, **kwargs)
48 except KeyboardInterrupt:
49 raise
50 except:
51 log.indented().trace(source=fn)
52 return (False, _Errs.IN_EXECUTION)
53 if not isinstance(res, dict):
54 return (False, _Errs.NON_DICT_RETURN)
55 return (True, res)