importlibあんまり丁寧に文章を書く気力が起きないので個人的なメモ。pythonのimport周りの話。fooがどうなるかみたいな話、そういえば発表のお題としてはけっこう良かったかもなーとか思ったりした。2019年7月24日ところでpythonのimportlibまわりは全体を追ってみるとへーと思うことが多かったりするので暇つぶしにはおすすめ。2019年7月24日はじめにある程度pythonのドキュメントを丁寧に読めば分かることではあるのだけれど。自分用の情報の整理のためにまとめる。このあたりを読めば良い。https://docs.python.org/ja/3/reference/import.html#the-import-systemhttps://docs.python.org/ja/3/library/importlib.htmlとくに1つ目のリンクでけっこう詳しく書かれているのだけれど。古い情報と今の情報が混在していて分かりづらい。ドキュメントがむずかしい以下の様なオブジェクトがいきなり出てきて何も知識なしに呼んでも正直何を言っているのかわからないと思う。わからないオブジェクトたち以下の様な役者がさらっとでてくる。finder)loaderimporterspecわからない設定値たちそしていきなり以下の様なsysモジュール以下の設定値のことが出てくる。sys.pathsys.meta_pathsys.path_hooks(sys.path_impoter_cache)いきなり情報が溢れてわからない。この辺を把握できるような感じの概観を使むような説明を書いてメモしておく。fooしたときに起きることpythonのコード上で以下の様なコードをインタプリタが解釈した時に何が起きるかということがあいまいにわかっていれば良い。そのための説明を雑に加えてみる。fooが解釈されるとき裏側では以下の様な処理が行われている。登録されているFinderを順に実行していくFinderのfind_spec()を実行するFinderのfinder_spec()がNone以外のものだったらそのspecを使って以降の処理を進めるspecからLoaderを取り出すLoaderのcreate_module()でモジュールを作るLoaderのexec_module()を実行する概要を擬似的なコードで雑な疑似コードに落とすとこういう形(ちょっとだけ例外の扱いも追加した。完全に正確なものではない)。ModuleNotFoundError(name)詳しい話はこの辺りにかかれているがたぶん最初に呼んでも分かりづらいかもしれない。https://docs.python.org/ja/3/reference/import.html#the-path-based-finderより詳細な疑似コードはこの辺りhttps://docs.python.org/ja/3/library/importlib.html#approximating-importlib-import-moduleただし実際に行われているコードはこれよりも古い実装への対応なども含めてもう少し複雑。以下の辺りのモジュールのコードを読むと良い。importlib.machneryになっている。finder,loader?以下の様な対応関係になっている(説明は用語集のものを引用)。発見を試行するオブジェクトです。モジュールをロードするオブジェクト。いう名前のメソッドを定義していなければなりません。classが定義されているのでこれを参照すると便利。思いきや、ここで定義されているメソッド群は古い仕様を尊重したものなので具合が悪い。これが古い仕様の混在がドキュメントにも存在していてけっこうツライ。deprecated実際のfinder,loader実際には以下の様なオブジェクトを期待している(後で余裕があればpepへのリンクを貼る)。module)だいぶわかりやすくなった。そんなわけで以下の様なイメージで覚えてくれれば良い。create_module()とexec_module()を実装しているもの新規に使う場合にはこういう捉え方で十分。これだけ覚えると良い。finderからloaderへの受け渡し実際先ほどのfinder,loaderの定義だけだとどうやってloaderが取り出されているかわからないと思う(実は用語集に言及があった(あとできづいた))。これはspecの作られ方を見てみると分かりやすい。(後々の操作でloaderがNoneのときにはちょっとした探索がはしったりもするけれどそれは省略)ここでimporterへの言及を。いうのはそういうことで典型的なimporterのfind_spec()は以下の様な実装になる。自分自身をloaderとして渡すということ(特に状態をもたない場合にはclassmethodとして実装されることもある)。...実際にimportの機能を提供しているものようやく具体的な話になってきた。これまででloader,finderとspecの説明が続いてきたが各種設定値についての説明がまだ。(sys.path)sys.meta_pathsys.path_hooks(sys.path_impoter_cache)それぞれの意味について説明する前に現在のpythonの環境で設定されている値について見てみよう。0x7f22f52e9598>]なにやらいろいろ登録されている。モジュールなどをimportして使われているloaderを調べる。0x7f22f5155b38>importlib.util.find_spec()でspecを取り出せる。origin='/usr/lib/python3.7/re.py')通常はSourceFileLoaderが使われると思う(ということが分かった)。色々なloader,finderその他には以下の様なクラスが用意されている。それこそimportlib.machineryを見れば良い。いままでの説明があればなんとなく予想がつくと思う。importlib.utilに存在するがdocumentに書いてある通り使いかたに注意が必要)通常使われているfinderさて、loaderが分かったが、finderがわからない。sys.path_hooksの対応がわからない。PathFinderFileFinderそれだけじゃどの設定値にどうやって設定されているのかわからないじゃん。実は以下の様な関係になっている。めんどくさいので関係をコードで表す。PathFinderのfind_spec()の中でFileLoaderが使われるそして、PathFinderのfind_spec()の中で、sys.path_hooksに格納されているfinderを見る。実際のコードはこんな感じ。None大抵の場合には、path_hooksで利用されるfinderはFileFinder。登録されているのは、これを生成するクロージャ関数になっている。補足するとsys.pathを気にするのはFileFinderの実装。これで全体が分かる様になってきたと思う。FileFinderはファイルの拡張子とLoaderの対応を取るのでこれを元にファイルシステムを探索してファイルを読み込みむという流れになる(.pyと.pycあるいは.soとかの拡張子に対応したloaderが選択される)。(こちらはPathFinderが利用する設定)ということになる(正確にはsys.path_hooksに登録されるのはfinderのfactory)。用語整理そろそろドキュメント本体を読むのも手軽になってきたと思うので用語の整理。これは[用語集をそのまま持ってきても良い気がするloaderモジュールをロードするオブジェクト。いう名前のメソッドを定義していなければなりません。