web-dev-qa-db-ja.com

__init__.pyを使用しても「非パッケージでの相対インポートの試行」を修正する方法

私は 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 でもうまく動かない"と " 相対パスからモジュールをインポートする "が見つかりましたが、役に立ちませんでした。

ここに足りないものはありますか。

668
skytreader

はい。あなたはそれをパッケージとして使っていません。

python -m pkg.tests.core_test
399

Ignacio Vazquez-Abramsについて answer:

Pythonのインポートメカニズムは、現在のファイルの__name__に対して機能します。ファイルを直接実行すると、通常の名前はありませんが、代わりに"__main__"がその名前として使用されます。そのため、相対インポートは機能しません。

Igancioが示唆しているように、-mオプションを使って実行することができます。スクリプトとして実行されることを意図したパッケージの一部がある場合は、__package__属性を使用して、そのファイルにパッケージ階層内の名前を指定することもできます。

詳細は http://www.python.org/dev/peps/pep-0366/ を参照してください。

586
BrenBarn

現在のディレクトリを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__))))
198
ihm

スクリプトの起動方法によって異なります。

コマンドラインから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
175
Paolo Rovelli

core_test.pyで、次の操作を行います。

import sys
sys.path.append('../components')
from core import GameLoopEvents
18
Allan Mwesigwa

あなたのユースケースがテストを実行するためのものであり、それがそうであることを継いでいるなら、あなたは以下をすることができます。テストスクリプトをpython core_test.pyとして実行する代わりに、pytestなどのテストフレームワークを使用します。その後、コマンドラインであなたが入力することができます

$$ py.test

それはあなたのディレクトリでテストを実行するでしょう。これは@BrenBarnによって指摘された__name____main__であるという問題を回避します。次に、空の__init__.pyファイルをテストディレクトリに置きます。これで、テストディレクトリがパッケージの一部になります。それであなたはできるようになるでしょう

from ..components.core import GameLoopEvents

ただし、テストスクリプトをメインプログラムとして実行すると、もう一度失敗します。テストランナーを使ってください。多分これはnosetestsのような他のテストランナーでも動作しますが、私はそれをチェックしていません。お役に立てれば。

9
deepak

私の簡単な解決策は、パスにディレクトリを追加することです。

import sys
sys.path.insert(0, '../components/')
7
v4gil

古いスレッド __init__.py ファイルに__all__= ['submodule', ...]を追加して、ターゲットでfrom <CURRENT_MODULE> import *を使用しても問題ないことがわかりました。

2
Laurent

あなたはfrom pkg.components.core import GameLoopEventsを使うことができます、例えば私はpycharmを使います、以下は私のプロジェクト構造イメージです、私はただルートパッケージからインポートします、そしてそれは動作します:

enter image description here

2
Jayhello

このアプローチは私にはうまくいき、いくつかの解決策よりも雑然としていません。

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

親ディレクトリは私のPYTHONPATHにあり、親ディレクトリとこのディレクトリには__init__.pyファイルがあります。

0
Rick Graves

これを試して

import components
from components import *
0
Vaishnavi Bala

誰かが回避策を探しているなら、私は1つにつまずいた。これはちょっとしたコンテキストです。ファイル内にあるメソッドの1つをテストしたいと思いました。内側から実行すると

if __== "__main__":

それは常に相対的な輸入を訴えた。上記の解決策を適用しようとしましたが、入れ子になったファイルが多数あり、それぞれに複数のインポートがあるため、うまくいきませんでした。

これが私がしたことです。必要なメソッドをインポートしてそれらを呼び出す外部プログラムであるランチャーを作成しました。素晴らしい解決策ではありませんが、うまくいきます。

0
HappyWaters

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はテスト済みです。

0
zhengcao