初めてのimportにHookして、モジュールパスをprintする
TL;DR
- Python2.7.141で、モジュールの初回Import後にフックして実行する仕組みが欲しかった。
- Python3系ではもっとスマートな方法があるはず。
- モジュールの初回Import時に読み込み元のファイルパスを表示するスクリプトを作成した。
- 本内容はAutodesk Mayaとは一切関係がないが、
MayaやHoudini、Blenderなど、Pythonインタプリタを利用しているツール上であればつきまとう(気がする)問題への、
一つの答えになりそうなのでタグを付けている。
モチベーション
- 長く運営が続いたプロジェクトは、内製ツールも増えていく。
ツールの利用状況を調べることで、不要になったツールをある程度削除することは可能だが、
ライブラリや設計が悪いと、スクリプトファイルの依存関係の把握すら満足に出来ず、
消したら何か環境が壊れることを恐れて2、削除できなくなってしまう。 - 不要なスクリプトファイルが増えることで、無駄なファイルの保守や引き継ぎコストが上がってしまうが、人手は足らなくなる一方。
- 初回Import時にHookする仕組みを作り、ファイル単位の利用状況を集計することで、
不要なスクリプトファイルを発見し、安易に削除できるようにして、無駄なコストを減らしたい。 - ついでにモジュールの読み込み元も把握しておきたい。
コード
firstimport_hook.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import __builtin__
import types
import copy
import sys
import inspect
# reload() 対策のため __import__ がBuiltinFunctionか確認した上で格納する。
if isinstance(__import__, types.BuiltinFunctionType):
_original__import__ = copy.deepcopy(__import__)
# 他のモジュールでimport hookされている場合などに、
# _original__import__ が定義出来ないので適当にエラーを吐く
elif '_original__import__' not in globals():
raise ImportError('Definition of _original__import__ failed.')
def __get_modulefile_from_load_module():
try:
# 読み込み元のモジュールファイルを取得する
currentframe = inspect.currentframe()
module_file = currentframe.f_back.f_back.f_code.co_filename
finally:
# 参照カウントを正しくするためにdel。
del currentframe
return module_file
def print_modulefile_post_first_import(*args, **kwargs):
"""初回インポート後にモジュールファイルを表示する"""
# importする前のモジュール読み込み状況を取得
loaded_module_names = sys.modules.keys()
# __import__を実行
module = _original__import__(*args, **kwargs)
# 既に一度ロードしたことがあったモジュールは処理しない
if module.__name__ in loaded_module_names:
return module
# __file__ がないモジュールのパスは表示しない
filepath = getattr(module, '__file__', None)
if filepath:
path_from_load_module = __get_modulefile_from_load_module()
print('%s -> %s' % (path_from_load_module, filepath))
return module
def enable_import_hook():
"""インポートフックを有効にする"""
__builtin__.__import__ = print_modulefile_post_first_import
def disable_import_hook():
"""インポートフックを無効にする"""
__builtin__.__import__ = _original__import__
if __name__ == '__main__':
# importフックを有効にする
enable_import_hook()
import json
reload(json)
startup.py
import firstimport_hook
firstimport_hook.enable_import_hook()
最後に
これで、Pythonファイルの初回Import時に、読み込んだモジュールのパスがprint()できるようになった。
あとは適当にサーバーに送信すれば集計なども可能だろう。
mel?知らない子ですね……