Pythonでディレクトリの上層にあるモジュールをimportするときの注意点

さて、今日は何かと忌み嫌われがちな「Pythonのimport文」についての記事を書きたいと思います。
その中でも特に、「今編集しているスクリプトより上層のディレクトリにあるモジュールをimportしたくなった」時にハマるケースについて取り上げます。


今、次のようなファイル構成でPythonのアプリケーション(モジュール)を書いていたとします。



my_module/A/A.pyを編集中, B/B.pyの機能が使いたくなったとします。
Pythonでは, from ... import ...の構文でimportを行う場合に
相対パスを指定する事ができるので、
my_module/B/__init__.pyに import Bと書いておき
from ..B import B と書く事でmy_module/B/B.pyをインポートすることにします。



ところが、ここで動作確認をしようとして、
Aディレクトリ上で python A.py などとすると噛まれます。

Traceback (most recent call last):
File "A.py", line 4, in
from ..B import B
ValueError: Attempted relative import beyond toplevel package


これは、Pythonの「現在実行されているファイルをモジュールのルートと見なす」
という仕様に起因するものです。
今、my_module/A 上でA.pyを実行したので、
インタプリタにとってはAディレクトリがルートとして認識されています。
なので、その上層にあるmy_moduleディレクトリはインタプリタにとっては
見えなくなっており、importに失敗してしまうという事です。


では、my_module/A/__init__.pyに import A と書いておき
今度はmy_moduleディレクトリ上でインタラクティブシェルを起動し、
import Aを実行
してみましょう。

Traceback (most recent call last):
File "", line 1, in
File "__init__.py", line 4, in
import A
ValueError: Attempted relative import beyond toplevel package

すると、やっぱり噛まれます。

Pythonでは、上図中の赤線に含まれる階層から実行しても同様にダメという決まりがあるようです。

なので、my_module/__init__.pyにもimport Aと書いておき
my_moduleが置かれているディレクトリ上インタラクティブシェルを実行して
import my_module すると先ほどのエラーが出なくなり、うまく動いていることが分かります。



すなわち、相対パスを用いたimportを使っているPythonプロジェクトでは、
動作確認にはそのプロジェクトのディレクトリの外にテスト用の
スクリプトを置いてそれを実行することになります。