I’ve tried a hacky way to get the col_offset of the symtable.
Then I can use the col_offset and the line number to find the ast node.
This works well. But is there any non-hacking way to solve this problem?
import ctypes
import symtable
Py_ssize_t = ctypes.c_int64 if ctypes.sizeof(ctypes.c_void_p) == 8 else ctypes.c_int32
class PyObject(ctypes.Structure):
_fields_ = [
("ob_refcnt", Py_ssize_t),
("ob_type", ctypes.c_void_p), # a pointer to type obj
]
class SymbolTableEntry(ctypes.Structure):
_fields_ = [
("ob_base", PyObject),
("ste_id", ctypes.py_object),
("ste_symbols", ctypes.py_object),
("ste_name", ctypes.py_object),
("ste_varnames", ctypes.py_object),
("ste_children", ctypes.py_object),
("ste_directives", ctypes.py_object),
("ste_type", ctypes.c_int), # enumerate type
("ste_nested", ctypes.c_int),
("ste_free", ctypes.c_uint, 1),
("ste_child_free", ctypes.c_uint, 1),
("ste_generator", ctypes.c_uint, 1),
("ste_coroutine", ctypes.c_uint, 1),
("ste_comprehension", ctypes.c_int), # enumerate type
("ste_varargs", ctypes.c_uint, 1),
("ste_varkeywords", ctypes.c_uint, 1),
("ste_returns_value", ctypes.c_uint, 1),
("ste_needs_class_closure", ctypes.c_uint, 1),
("ste_comp_iter_target", ctypes.c_uint, 1),
("ste_comp_iter_expr", ctypes.c_int),
("ste_lineno", ctypes.c_int),
("ste_col_offset", ctypes.c_int),
("ste_end_lineno", ctypes.c_int),
("ste_end_col_offset", ctypes.c_int),
("ste_opt_lineno", ctypes.c_int),
("ste_opt_col_offset", ctypes.c_int),
("ste_table", ctypes.c_void_p), # a pointer to the symbol table struct
]
def get_symtable_col_offset(symt: symtable.symtable):
p_ste = ctypes.cast(
ctypes.c_void_p(id(symt._table)),
ctypes.POINTER(SymbolTableEntry),
)
return p_ste.contents.ste_col_offset
script = """
[[i for i in range(10)],[j for j in range(10)]]
"""
symt = symtable.symtable(script, "test.py", "exec")
symt = symt.get_children()[1]
print(get_symtable_col_offset(symt)) # 24