私は PEP 328 に従うことを試みています、そして以下のディレクトリ構造で:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
core_test.py
に私は次のようなimport文があります
from ..components.core import GameLoopEvents
しかし、実行すると、次のようなエラーが表示されます。
tests$ python core_test.py
Traceback (most recent call last):
File "core_test.py", line 3, in <module>
from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package
周りを検索してみると、 " 相対パスは__init__.py でもうまく動かない"と " 相対パスからモジュールをインポートする "が見つかりましたが、役に立ちませんでした。
ここに足りないものはありますか。
はい。あなたはそれをパッケージとして使っていません。
python -m pkg.tests.core_test
Ignacio Vazquez-Abramsについて answer:
Pythonのインポートメカニズムは、現在のファイルの__name__
に対して機能します。ファイルを直接実行すると、通常の名前はありませんが、代わりに"__main__"
がその名前として使用されます。そのため、相対インポートは機能しません。
Igancioが示唆しているように、-m
オプションを使って実行することができます。スクリプトとして実行されることを意図したパッケージの一部がある場合は、__package__
属性を使用して、そのファイルにパッケージ階層内の名前を指定することもできます。
詳細は http://www.python.org/dev/peps/pep-0366/ を参照してください。
現在のディレクトリをimport components.core
に追加する場合は、sys.path
を直接使用できます。
if __== '__main__' and __package__ is None:
from os import sys, path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
スクリプトの起動方法によって異なります。
コマンドラインからUnitTestを起動する を従来の方法で実行する場合、次のようになります。
python tests/core_test.py
次に、この場合 'components'および 'tests'は兄弟フォルダーであるため、次のいずれかを使用して相対モジュールをインポートできます。 insertまたはappendメソッドのsys.pathモジュール。何かのようなもの:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
それ以外の場合は、 '-m'引数を使用してスクリプトを起動します (この場合、パッケージについて話しているため、を指定しないでください) .py '拡張子)、つまり:
python -m pkg.tests.core_test
そのような場合、あなたはやっていたように単純に相対インポートを使うことができます:
from ..components.core import GameLoopEvents
最終的に2つのアプローチを組み合わせて、スクリプトがどのように呼び出されても機能するようにすることができます。例えば:
if __== '__main__':
if __package__ is None:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
else:
from ..components.core import GameLoopEvents
core_test.pyで、次の操作を行います。
import sys
sys.path.append('../components')
from core import GameLoopEvents
あなたのユースケースがテストを実行するためのものであり、それがそうであることを継いでいるなら、あなたは以下をすることができます。テストスクリプトをpython core_test.py
として実行する代わりに、pytest
などのテストフレームワークを使用します。その後、コマンドラインであなたが入力することができます
$$ py.test
それはあなたのディレクトリでテストを実行するでしょう。これは@BrenBarnによって指摘された__name__
が__main__
であるという問題を回避します。次に、空の__init__.py
ファイルをテストディレクトリに置きます。これで、テストディレクトリがパッケージの一部になります。それであなたはできるようになるでしょう
from ..components.core import GameLoopEvents
ただし、テストスクリプトをメインプログラムとして実行すると、もう一度失敗します。テストランナーを使ってください。多分これはnosetests
のような他のテストランナーでも動作しますが、私はそれをチェックしていません。お役に立てれば。
私の簡単な解決策は、パスにディレクトリを追加することです。
import sys
sys.path.insert(0, '../components/')
古いスレッド __init__.py ファイルに__all__= ['submodule', ...]
を追加して、ターゲットでfrom <CURRENT_MODULE> import *
を使用しても問題ないことがわかりました。
このアプローチは私にはうまくいき、いくつかの解決策よりも雑然としていません。
try:
from ..components.core import GameLoopEvents
except ValueError:
from components.core import GameLoopEvents
親ディレクトリは私のPYTHONPATHにあり、親ディレクトリとこのディレクトリには__init__.py
ファイルがあります。
これを試して
import components
from components import *
誰かが回避策を探しているなら、私は1つにつまずいた。これはちょっとしたコンテキストです。ファイル内にあるメソッドの1つをテストしたいと思いました。内側から実行すると
if __== "__main__":
それは常に相対的な輸入を訴えた。上記の解決策を適用しようとしましたが、入れ子になったファイルが多数あり、それぞれに複数のインポートがあるため、うまくいきませんでした。
これが私がしたことです。必要なメソッドをインポートしてそれらを呼び出す外部プログラムであるランチャーを作成しました。素晴らしい解決策ではありませんが、うまくいきます。
Paolo が言ったように、2つの呼び出しメソッドがあります。
1) python -m tests.core_test
2) python tests/core_test.py
それらの間の1つの違いはsys.path [0]文字列です。 import を実行すると、インタプリタはsys.pathを検索するので、tests/core_test.py
を使用して実行できます。
if __== '__main__':
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from components import core
<other stuff>
そして、これ以降は、他の方法でcore_test.pyを実行することができます。
cd tests
python core_test.py
python -m core_test
...
注意、py36はテスト済みです。