| 65 | self._code_cache: dict[tuple[str, int, str], types.CodeType] = {} |
| 66 | |
| 67 | def _get_cached_code_object(self, filename: str, lineno: int, func: str) -> types.CodeType: |
| 68 | # Hack to create a code object that points to the correct |
| 69 | # line number and function name |
| 70 | key = (filename, lineno, func) |
| 71 | # cache the code object to avoid re-creating it |
| 72 | if key in self._code_cache: |
| 73 | return self._code_cache[key] |
| 74 | # Parse to AST and zero out column info |
| 75 | # since column info are not accurate in original trace |
| 76 | tree = ast.parse("_getframe()", filename=filename, mode="eval") |
| 77 | for node in ast.walk(tree): |
| 78 | if hasattr(node, "col_offset"): |
| 79 | node.col_offset = 0 # ty: ignore[invalid-assignment] |
| 80 | if hasattr(node, "end_col_offset"): |
| 81 | node.end_col_offset = 0 # ty: ignore[invalid-assignment] |
| 82 | # call into get frame, bt changes the context |
| 83 | code_object = compile(tree, filename, "eval") |
| 84 | # replace the function name and line number |
| 85 | code_object = code_object.replace(co_name=func, co_firstlineno=lineno) |
| 86 | self._code_cache[key] = code_object |
| 87 | return code_object |
| 88 | |
| 89 | def _create_frame(self, filename: str, lineno: int, func: str) -> types.FrameType: |
| 90 | """Create a frame object from the filename, lineno, and func.""" |