はじめに
Pythonの理解を深めるために、スタックフレームからどんな情報が得られるかを調査した。
思いの外大量の情報が得られてカオスになったので、目視できる程度の情報を雑にprint()
で表示してみた。
環境
$ python3 --version Python 3.10.12
コード
コードが雑なので、説明も雑に。。。 スタックフレームのオブジェクトが持つインスタンス変数を表示する。 直接関係ない情報や大量に表示される情報(特に変数とバイトコード)もあるので、ある程度絞り込みを行っている。 中には無限再帰してしまうものもあったので、そういうものは適当に除外している。
import inspect import json import collections.abc from typing import Any exclude_preds = { 'frame': lambda name, value: (not name.startswith('f_')) or (name in ['f_back', 'f_builtins', 'f_globals', 'f_locals']), 'f_code': lambda name, value: (not name.startswith('co_')) or (name in ['co_varnames', 'co_cellvars', 'co_freevars', 'co_code', 'co_consts', 'co_names', 'co_lnotab', 'co_linetable']) } def default_exclude_pred(name: str, value: Any) -> bool: return name.startswith('_') def __collect_stackframe_details(name: str | None, value: Any) -> None | bool | int | float | str | list | dict: if isinstance(value, None | bool | int | float | str): return value elif isinstance(value, list): return [__collect_stackframe_details(None, entry) for entry in value] else: result: dict[str, Any] = {} exclude_pred = exclude_preds[name] if name in exclude_preds else default_exclude_pred for n, v in (value.items() if isinstance(value, collections.abc.Mapping) else inspect.getmembers(value)): if exclude_pred(n, v): continue result[n] = __collect_stackframe_details(n, v) return result def collect_stackframe_details() -> list: result = [] for stack_frame in inspect.stack(): result.append(__collect_stackframe_details(None, stack_frame)) return result print(json.dumps(collect_stackframe_details(), indent=4))
実行結果
$ python3 stack_frame_details.py [ { "code_context": [ " for stack_frame in inspect.stack():\n" ], "count": {}, "filename": "/home/jaybanuan/src/stack_frame_details/stack_frame_ditails.py", "frame": { "f_code": { "co_argcount": 0, "co_filename": "/home/jaybanuan/src/stack_frame_details/stack_frame_ditails.py", "co_firstlineno": 36, "co_flags": 67, "co_kwonlyargcount": 0, "co_lines": {}, "co_name": "collect_stackframe_details", "co_nlocals": 2, "co_posonlyargcount": 0, "co_stacksize": 6 }, "f_lasti": 26, "f_lineno": 40, "f_trace": null, "f_trace_lines": true, "f_trace_opcodes": false }, "function": "collect_stackframe_details", "index": 0, "lineno": 39 }, { "code_context": [ "print(json.dumps(collect_stackframe_details(), indent=4)) \n" ], "count": {}, "filename": "/home/jaybanuan/src/stack_frame_details/stack_frame_ditails.py", "frame": { "f_code": { "co_argcount": 0, "co_filename": "/home/jaybanuan/src/stack_frame_details/stack_frame_ditails.py", "co_firstlineno": 1, "co_flags": 64, "co_kwonlyargcount": 0, "co_lines": {}, "co_name": "<module>", "co_nlocals": 0, "co_posonlyargcount": 0, "co_stacksize": 7 }, "f_lasti": 148, "f_lineno": 45, "f_trace": null, "f_trace_lines": true, "f_trace_opcodes": false }, "function": "<module>", "index": 0, "lineno": 45 } ]
実はパッケージの情報が欲しかった
スタックフレームの中身を確認したかったのは、各スタックフレームがどのパッケージ由来なのかを知りたかったのだが、直接的な情報を見つけられなかった。 手元では試してないが、すごく頑張って似たような情報を構築している例を見つけた。