|
1 | | - |
2 | 1 | import importlib |
3 | 2 | import logging |
4 | 3 | import shutil |
|
12 | 11 | logger = logging.getLogger(__name__) |
13 | 12 |
|
14 | 13 |
|
15 | | -class DeepReloader: |
16 | | - """再帰的なモジュールリロードを行うクラス。 |
| 14 | +def deep_reload(module: ModuleType) -> None: |
| 15 | + """モジュールを再帰的にリロードする。 |
17 | 16 |
|
18 | 17 | Maya開発でのモジュール変更を即座に反映させるために設計されています。 |
| 18 | +
|
| 19 | + Args: |
| 20 | + module: リロード対象のモジュール |
| 21 | +
|
| 22 | + Note: |
| 23 | + ログレベルの設定には setup_logging() 関数を使用してください。 |
| 24 | + 例: setup_logging(logging.DEBUG) |
19 | 25 | """ |
| 26 | + # キャッシュを無効化して .py の変更を認識させる |
| 27 | + importlib.invalidate_caches() |
| 28 | + |
| 29 | + # TODO: パフォーマンス最適化 - ファイル変更検出による差分リロード |
| 30 | + # - ファイルのタイムスタンプキャッシュで変更検出 |
| 31 | + # - 変更されたモジュールのみのリロード(現在は全モジュール対象) |
| 32 | + # - AST解析結果のキャッシュ(頻繁にアクセスされるモジュール用) |
| 33 | + # - 依存関係ツリーのキャッシュ(構造変更時のみ再構築) |
| 34 | + |
| 35 | + # ツリー構築(まずツリーを構築して全モジュールを把握) |
| 36 | + # TODO: ツリー構造のデバッグ出力機能を追加 |
| 37 | + # - 依存関係ツリーの視覚的表示(階層構造、インデント付き) |
| 38 | + # - 各モジュールの詳細情報(パス、サイズ、最終更新時刻) |
| 39 | + # - スキップされるモジュールの理由と一覧 |
| 40 | + root = _build_tree(module) |
| 41 | + |
| 42 | + # ツリー全体の __pycache__ を削除 |
| 43 | + _clear_pycache_recursive(root) |
| 44 | + |
| 45 | + # 親→子へリロード |
| 46 | + # TODO: リロード順序とプロセスの詳細ログ追加 |
| 47 | + # - 各モジュールのリロード開始/完了タイミング |
| 48 | + # - リロード中のエラーとリカバリ状況 |
| 49 | + # - パフォーマンス情報(各段階の実行時間) |
| 50 | + root.reload() |
| 51 | + |
| 52 | + # 子→親へシンボルをコピー(from-import で取得したシンボルを親モジュールに反映) |
| 53 | + # TODO: シンボルコピー処理の詳細ログ追加 |
| 54 | + # - コピーされるシンボルの詳細(名前、型、ソース) |
| 55 | + # - シンボルの競合や上書き状況 |
| 56 | + # - 失敗したシンボルとその理由 |
| 57 | + root.overwrite_symbols() |
| 58 | + |
| 59 | + |
| 60 | +def _build_tree(module: ModuleType) -> ModuleInfo: |
| 61 | + """ |
| 62 | + AST 解析して ModuleInfo ツリーを構築 |
| 63 | +
|
| 64 | + TODO: 組み込みモジュール(os、pathlib等)やサードパーティライブラリ(maya.cmds、PySide6等)の |
| 65 | + スキップ処理を実装する必要がある。現在は全ての依存関係をリロード対象としているため、 |
| 66 | + 不要なリロードや潜在的な危険性がある。 |
| 67 | + """ |
| 68 | + |
| 69 | + # 念のため sys.modules から最新の正規モジュールを取得する |
| 70 | + module = sys.modules[module.__name__] |
| 71 | + |
| 72 | + node = ModuleInfo(module) |
| 73 | + |
| 74 | + extractor = SymbolExtractor(module) |
| 75 | + for child_module, symbols in extractor.extract(): |
| 76 | + child_node = _build_tree(child_module) |
| 77 | + child_node.symbols = symbols |
| 78 | + node.children.append(child_node) |
20 | 79 |
|
21 | | - def __init__(self) -> None: |
22 | | - """DeepReloader を初期化する。 |
23 | | -
|
24 | | - Note: |
25 | | - ログレベルの設定には setup_logging() 関数を使用してください。 |
26 | | - 例: setup_logging(logging.DEBUG) |
27 | | - """ |
28 | | - pass |
29 | | - |
30 | | - def reload(self, module: ModuleType) -> None: |
31 | | - # キャッシュを無効化して .py の変更を認識させる |
32 | | - importlib.invalidate_caches() |
33 | | - |
34 | | - # TODO: パフォーマンス最適化 - ファイル変更検出による差分リロード |
35 | | - # - ファイルのタイムスタンプキャッシュで変更検出 |
36 | | - # - 変更されたモジュールのみのリロード(現在は全モジュール対象) |
37 | | - # - AST解析結果のキャッシュ(頻繁にアクセスされるモジュール用) |
38 | | - # - 依存関係ツリーのキャッシュ(構造変更時のみ再構築) |
39 | | - |
40 | | - # ツリー構築(まずツリーを構築して全モジュールを把握) |
41 | | - # TODO: ツリー構造のデバッグ出力機能を追加 |
42 | | - # - 依存関係ツリーの視覚的表示(階層構造、インデント付き) |
43 | | - # - 各モジュールの詳細情報(パス、サイズ、最終更新時刻) |
44 | | - # - スキップされるモジュールの理由と一覧 |
45 | | - root = self._build_tree(module) |
46 | | - |
47 | | - # ツリー全体の __pycache__ を削除 |
48 | | - self._clear_pycache_recursive(root) |
49 | | - |
50 | | - # 親→子へリロード |
51 | | - # TODO: リロード順序とプロセスの詳細ログ追加 |
52 | | - # - 各モジュールのリロード開始/完了タイミング |
53 | | - # - リロード中のエラーとリカバリ状況 |
54 | | - # - パフォーマンス情報(各段階の実行時間) |
55 | | - root.reload() |
56 | | - |
57 | | - # 子→親へシンボルをコピー(from-import で取得したシンボルを親モジュールに反映) |
58 | | - # TODO: シンボルコピー処理の詳細ログ追加 |
59 | | - # - コピーされるシンボルの詳細(名前、型、ソース) |
60 | | - # - シンボルの競合や上書き状況 |
61 | | - # - 失敗したシンボルとその理由 |
62 | | - root.overwrite_symbols() |
63 | | - |
64 | | - def _build_tree(self, module: ModuleType) -> ModuleInfo: |
65 | | - """ |
66 | | - AST 解析して ModuleInfo ツリーを構築 |
67 | | - |
68 | | - TODO: 組み込みモジュール(os、pathlib等)やサードパーティライブラリ(maya.cmds、PySide6等)の |
69 | | - スキップ処理を実装する必要がある。現在は全ての依存関係をリロード対象としているため、 |
70 | | - 不要なリロードや潜在的な危険性がある。 |
71 | | - """ |
72 | | - |
73 | | - # 念のため sys.modules から最新の正規モジュールを取得する |
74 | | - module = sys.modules[module.__name__] |
75 | | - |
76 | | - node = ModuleInfo(module) |
77 | | - |
78 | | - extractor = SymbolExtractor(module) |
79 | | - for child_module, symbols in extractor.extract(): |
80 | | - child_node = self._build_tree(child_module) |
81 | | - child_node.symbols = symbols |
82 | | - node.children.append(child_node) |
83 | | - |
84 | | - return node |
85 | | - |
86 | | - def _clear_pycache_recursive(self, node: ModuleInfo) -> None: |
87 | | - """ |
88 | | - ModuleInfo ツリー全体を再帰的にたどって __pycache__ を削除 |
89 | | - """ |
90 | | - self._clear_single_pycache(node.module) |
91 | | - for child in node.children: |
92 | | - self._clear_pycache_recursive(child) |
93 | | - |
94 | | - def _clear_single_pycache(self, module: ModuleType) -> None: |
95 | | - """ |
96 | | - 1つのモジュールに対応する __pycache__ を削除 |
97 | | - """ |
98 | | - module_file = getattr(module, '__file__', None) |
99 | | - if module_file is None: |
100 | | - return |
101 | | - |
102 | | - module_dir = Path(module_file).parent |
103 | | - pycache_dir = module_dir / '__pycache__' |
104 | | - |
105 | | - if pycache_dir.exists(): |
106 | | - try: |
107 | | - shutil.rmtree(pycache_dir) |
108 | | - logger.debug(f'Cleared pycache {pycache_dir}') |
109 | | - except Exception as e: |
110 | | - logger.warning(f'Failed to clear pycache {pycache_dir}: {e!r}') |
111 | | - |
112 | | - # TODO: 将来の実装用メソッド - デバッグ情報の出力機能 |
113 | | - # def _print_tree_structure(self, root: ModuleInfo, level: int = 0) -> None: |
114 | | - # """依存関係ツリーを視覚的に表示する |
115 | | - # |
116 | | - # 出力例: |
117 | | - # my_package.main |
118 | | - # ├── my_package.utils (from utils import helper, calculator) |
119 | | - # │ └── my_package.math_utils (from .math_utils import add, subtract) |
120 | | - # ├── my_package.config (from .config import settings) |
121 | | - # └── os [SKIPPED: builtin module] |
122 | | - # """ |
123 | | - # pass |
124 | | - # |
125 | | - # def _log_reload_summary(self, root: ModuleInfo) -> None: |
126 | | - # """リロード処理の概要を詳細ログ出力 |
127 | | - # |
128 | | - # 出力内容: |
129 | | - # - 処理開始/終了時刻 |
130 | | - # - 総モジュール数、リロード対象数、スキップ数 |
131 | | - # - 各段階の所要時間(ツリー構築、リロード、シンボルコピー) |
132 | | - # - 検出されたエラーや警告の統計 |
133 | | - # """ |
134 | | - # pass |
135 | | - # |
136 | | - # TODO: 将来の実装用 - パフォーマンス最適化キャッシュシステム |
137 | | - # def _init_cache_system(self) -> None: |
138 | | - # """キャッシュシステムの初期化 |
139 | | - # |
140 | | - # キャッシュ対象: |
141 | | - # - ファイルタイムスタンプ(変更検出用) |
142 | | - # - AST解析結果(重いパース処理の削減) |
143 | | - # - 依存関係ツリー(構造変更時のみ再構築) |
144 | | - # - モジュール判定結果(組み込み/サードパーティ判定キャッシュ) |
145 | | - # """ |
146 | | - # pass |
147 | | - # |
148 | | - # def _should_reload_module(self, module: ModuleType) -> bool: |
149 | | - # """モジュールがリロード必要かキャッシュベースで判定 |
150 | | - # |
151 | | - # 判定基準: |
152 | | - # - ファイルタイムスタンプの変更 |
153 | | - # - 依存関係の変更 |
154 | | - # - 強制リロードフラグ |
155 | | - # """ |
156 | | - # pass |
| 80 | + return node |
| 81 | + |
| 82 | + |
| 83 | +def _clear_pycache_recursive(node: ModuleInfo) -> None: |
| 84 | + """ |
| 85 | + ModuleInfo ツリー全体を再帰的にたどって __pycache__ を削除 |
| 86 | + """ |
| 87 | + _clear_single_pycache(node.module) |
| 88 | + for child in node.children: |
| 89 | + _clear_pycache_recursive(child) |
| 90 | + |
| 91 | + |
| 92 | +def _clear_single_pycache(module: ModuleType) -> None: |
| 93 | + """ |
| 94 | + 1つのモジュールに対応する __pycache__ を削除 |
| 95 | + """ |
| 96 | + module_file = getattr(module, '__file__', None) |
| 97 | + if module_file is None: |
| 98 | + return |
| 99 | + |
| 100 | + module_dir = Path(module_file).parent |
| 101 | + pycache_dir = module_dir / '__pycache__' |
| 102 | + |
| 103 | + if pycache_dir.exists(): |
| 104 | + try: |
| 105 | + shutil.rmtree(pycache_dir) |
| 106 | + logger.debug(f'Cleared pycache {pycache_dir}') |
| 107 | + except Exception as e: |
| 108 | + logger.warning(f'Failed to clear pycache {pycache_dir}: {e!r}') |
| 109 | + |
| 110 | + |
| 111 | +# TODO: 将来の実装用メソッド - デバッグ情報の出力機能 |
| 112 | +# def _print_tree_structure(root: ModuleInfo, level: int = 0) -> None: |
| 113 | +# """依存関係ツリーを視覚的に表示する |
| 114 | +# |
| 115 | +# 出力例: |
| 116 | +# my_package.main |
| 117 | +# ├── my_package.utils (from utils import helper, calculator) |
| 118 | +# │ └── my_package.math_utils (from .math_utils import add, subtract) |
| 119 | +# ├── my_package.config (from .config import settings) |
| 120 | +# └── os [SKIPPED: builtin module] |
| 121 | +# """ |
| 122 | +# pass |
| 123 | +# |
| 124 | +# def _log_reload_summary(root: ModuleInfo) -> None: |
| 125 | +# """リロード処理の概要を詳細ログ出力 |
| 126 | +# |
| 127 | +# 出力内容: |
| 128 | +# - 処理開始/終了時刻 |
| 129 | +# - 総モジュール数、リロード対象数、スキップ数 |
| 130 | +# - 各段階の所要時間(ツリー構築、リロード、シンボルコピー) |
| 131 | +# - 検出されたエラーや警告の統計 |
| 132 | +# """ |
| 133 | +# pass |
| 134 | +# |
| 135 | +# TODO: 将来の実装用 - パフォーマンス最適化キャッシュシステム |
| 136 | +# def _init_cache_system() -> None: |
| 137 | +# """キャッシュシステムの初期化 |
| 138 | +# |
| 139 | +# キャッシュ対象: |
| 140 | +# - ファイルタイムスタンプ(変更検出用) |
| 141 | +# - AST解析結果(重いパース処理の削減) |
| 142 | +# - 依存関係ツリー(構造変更時のみ再構築) |
| 143 | +# - モジュール判定結果(組み込み/サードパーティ判定キャッシュ) |
| 144 | +# """ |
| 145 | +# pass |
| 146 | +# |
| 147 | +# def _should_reload_module(module: ModuleType) -> bool: |
| 148 | +# """モジュールがリロード必要かキャッシュベースで判定 |
| 149 | +# |
| 150 | +# 判定基準: |
| 151 | +# - ファイルタイムスタンプの変更 |
| 152 | +# - 依存関係の変更 |
| 153 | +# - 強制リロードフラグ |
| 154 | +# """ |
| 155 | +# pass |
0 commit comments