hdl.rec: use a helpful error on unknown field reference.
[nmigen.git] / nmigen / tracer.py
1 import traceback
2 import inspect
3 from opcode import opname
4
5
6 __all__ = ["NameNotFound", "get_var_name", "get_src_loc"]
7
8
9 class NameNotFound(Exception):
10 pass
11
12
13 def get_var_name(depth=2):
14 frame = inspect.currentframe()
15 for _ in range(depth):
16 frame = frame.f_back
17
18 code = frame.f_code
19 call_index = frame.f_lasti
20 call_opc = opname[code.co_code[call_index]]
21 if call_opc not in ("CALL_FUNCTION", "CALL_FUNCTION_KW", "CALL_FUNCTION_EX", "CALL_METHOD"):
22 return None
23
24 index = call_index + 2
25 while True:
26 opc = opname[code.co_code[index]]
27 if opc in ("STORE_NAME", "STORE_ATTR"):
28 name_index = int(code.co_code[index + 1])
29 return code.co_names[name_index]
30 elif opc == "STORE_FAST":
31 name_index = int(code.co_code[index + 1])
32 return code.co_varnames[name_index]
33 elif opc == "STORE_DEREF":
34 name_index = int(code.co_code[index + 1])
35 return code.co_cellvars[name_index]
36 elif opc in ("LOAD_GLOBAL", "LOAD_ATTR", "LOAD_FAST", "LOAD_DEREF",
37 "DUP_TOP", "BUILD_LIST"):
38 index += 2
39 else:
40 raise NameNotFound
41
42
43 def get_src_loc(src_loc_at=0):
44 # n-th frame: get_src_loc()
45 # n-1th frame: caller of get_src_loc() (usually constructor)
46 # n-2th frame: caller of caller (usually user code)
47 # Python returns the stack frames reversed, so it is enough to set limit and grab the very
48 # first one in the array.
49 tb = traceback.extract_stack(limit=3 + src_loc_at)
50 return (tb[0].filename, tb[0].lineno)