Pixel Pedals of Tomakomai

北海道苫小牧市出身の初老の日常

Import hooks 内で投げた例外

class MyImporter(object):
    def find_module(self, fullname, path=None): return self
    def load_module(self, fullname): raise ImportError("failed")

import sys
sys.meta_path = [MyImporter()]

import datetime

だと「ImportError: failed」になって、

class MyImporter(object):
    def find_module(self, fullname, path=None): raise ImportError("failed")

だと「ImportError: No module named datetime」になって、

class MyImporter(object):
    def find_module(self, fullname, path=None): raise Exception("failed")

だと「Exception: failed」になるのは、find_module でImportErrorを上げた時だけ特別扱いされてるってことでしょうか。PEP-302には "If find_module() raises an exception, it will be propagated to the caller, aborting the import." としか書かれてないみたいですけど。

1/9追記

import.c をざっくり追った感じだと、load_packageやimport_submoduleの中で、find_module内で上げられたPyExc_ImportError は捨ててるっぽいですねー。で、return Py_None してることからわかりますが、 load_module から None を返した場合もこれと同等の挙動になります。ただし、これも PEP-302 的には未定義なので、やるべきじゃないんでしょう。

static PyObject *
import_submodule(PyObject *mod, char *subname, char *fullname)
{
..略..
        fdp = find_module(fullname, subname, path, buf, MAXPATHLEN+1,
                          &fp, &loader);
        Py_XDECREF(path);
        if (fdp == NULL) {
            if (!PyErr_ExceptionMatches(PyExc_ImportError))
                return NULL;
            PyErr_Clear();
            Py_INCREF(Py_None);
            return Py_None;
        }
..略..
        m = load_module(fullname, fp, buf, fdp->type, loader);
..略..
    return m;
}

import.c

なお、path_hooks を実行した際に上げられたImportError はget_path_importer 内で捕捉されて無視されています。これはPEP-302 に書かれている通りの挙動ですね。