LoginSignup
3
1

More than 1 year has passed since last update.

[python]importについて理解したい件―pytest, aws lambdaで使う

Posted at

TL;DR

tests/conftest.py でデプロイ用のlambdaディレクトリをpathに追加する必要がある。

sys.path.append(os.path.join(os.dirname(__file__), '../{lambdaコードのディレクトリ}')

はじめに

pytestでテストを実行した際に、インポートエラーに遭遇した。
importについての理解が浅いせいで、アホみたいな勘違いをして時間を浪費したのでメモる。
sys.path.append は定義していたのだが、通すpathが間違っていたので苦労した。

ディレクトリ構成

test_app.ppy[app.py](http://app.py) をテストするときに mypkg をimportしたいケースを考えます。
パッケージ(ディレクトリ?)にはアンダースコア(_)は非推奨らしいので、 lambadasrc とした。また、ハイフン(-)を利用する場合、パッケージ化したときにimport文で読み込めなくなるので、命名について理解する必要がある。。。

.
├── lambadasrc
│   ├── app.py
│   └── mypkg
│       └── module1.py
└── tests
    ├── __init__.py
    ├── conftest.py
    └── test_app.py

エラー内容

pytestを実行するとエラー出力される。

.venv ❯ pytest tests -s

~
lambadasrc/app.py:8: in import_mypkg
    from mypkg.module1 import func
E   ModuleNotFoundError: No module named 'mypkg'

app.pyは下記。
lambadasrc ディレクトリは、lambdaのデプロイ単位のディレクトリと考えて、その直下からimportする。

from pprint import pprint
import sys
pprint(sys.path)

def import_mypkg():
    from mypkg.module1 import func
    return func

func = import_mypkg()

def lambada_handler(event, context):
    func()

なぜエラーになるかというと、 sys.pathlambdasrc へのPATHが通っていないため。(おそらく。。)

['/Users/hoge/import_pytest_202203',
 '/Users/hoge/import_pytest_202203/.venv/bin',
 '/Users/hoge/.pyenv/versions/3.9.7/lib/python39.zip',
 '/Users/hoge/.pyenv/versions/3.9.7/lib/python3.9',
 '/Users/hoge/.pyenv/versions/3.9.7/lib/python3.9/lib-dynload',
 '/Users/hoge/import_pytest_202203/.venv/lib/python3.9/site-packages']

pytestはtestのソースコードから上の階層にむかって __init__.py の有無でベースディレクトリを判断する仕様になっているので、 /Users/hoge/import_pytest_202203 が定義されていると理解。
しかし、 lambdasrc まではpathが通っていないため、import エラーが発生する。
なので、conftest.pyに sys.path.append をかく。

import sys
import os

sys.path.append(os.path.join(os.path.dirname(__file__), '../lambadasrc'))

sys.pathに下記が追加される。testを実行してもエラーは発生しなくなる。

'/Users/hoge/import_pytest_202203/tests/../lambadasrc',

勘違いしてたこと、色々調べてさらに沼ったこと

  • sys.path.append で、 lambadasrc/mypkg にpathを通していた
    • 何故かimportするパッケージまでpathが通っている必要があると思いこんでた。。。
  • __init__.py の有無
    • pytestでは、ベースディレクトリの判断のために必要
    • srcでは、レギュラーパッケージとして認識させる場合は__init__.pyが必要
    • 暗黙的な名前空間パッケージ(Implicit Namespace Packages)として利用するなら不要
      • まぁ、パフォーマンス的に定義してほうがよいという記事もあるが、、
    • __init__.py__all__ 定義している情報も見かけ、更に混乱した。。。
      • __all__の用途は、from~importのワイルドカードを制御するために使用。また、途中のmodule名を省略するために利用しているケースも観測
      • from mypkg.hoge from Hoge → from mypkg from Hoge(hogeを省略)
    • setup.py(公式では、pyproject.toml,setup.cfg)を使用して、ローカルモジュールとしてpathを通す解決策もあり?
      • pip install -e . で編集モードでパッケージ作成
      • 上記試したが、解決できなかった
      • おそらく、本来はパッケージ開発にpytestをインテグレーションするトピックだと推測。(src/とtest/のディレクトリ構成もパッケージ開発のためのトピックか?)
      • https://docs.pytest.org/en/latest/explanation/goodpractices.html#src-layout
  • namespaceもよくわかってない
  • デバッグと単純なRunだと、挙動が違いみたい?

他の対応案?

  • pytest コマンド実行でなく、 python -m pytest 実行だとカレントディレクトリをsys.pathに追加するっぽい
  • importlib がちょっと気になる

importlib: new in pytest-6.0, this mode uses [importlib](https://docs.python.org/3/library/importlib.html#module-importlib) to import test modules. This gives full control over the import process, and doesn’t require changing [sys.path](https://docs.python.org/3/library/sys.html#sys.path).

終わりに

今回のエラーについてググると、ヒットする記事の内容に微妙に差異があるので自分のエラー原因を特定するのに苦労した。ググり力を高めていきたい。

参考

pytest import mechanisms and sys.path/PYTHONPATH - pytest documentation

Good Integration Practices - pytest documentation

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1