Python自作パッケージのunittestをそれっぽいディレクトリ構成で実施する際にModuleNotFoundErrorを回避する
はじめに
- Pythonパッケージ(Pypiで公開)を作成しようとしていて
- かつそれっぽいPythonディレクトリ構成でリリースを検討した際にハマった点を備忘として記録
- あとはIDE(Pycharm)様々という感謝の気持ち
こんなエラー・事象にハマった
パッケージ構成
-
再現可能なテストを実行するために、かつ公開パッケージとして適切なディレクトリ構造にするために作成するパッケージのディレクトリとテストディレクトリは分類し、以下のようなパッケージ構成にしています。
- 偉い人がそう言っている
- 世の素晴らしい公開パッケージの構成もそうなっているため
パッケージ構成
Macintosh:$ tree
Project
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── docs
│ ├── Makefile
│ ├── conf.py
│ ├── index.rst
│ └── make.bat
├── setup.py
├── my_package_name
│ ├── __init__.py
│ ├── my_package_core.py
│ └── my_package_helper.py
└── tests
├── __init__.py
├── test_my_package_helper.py
└── test_my_package_core.py
いざテスト
- testsディレクトリ配下のtest_my_package_core.pyのテストを実装(my_package_nameのmy_package_core.pyのあるクラスのみをimport)し、ターミナルからテスト実行しようとしたところ、エラーが発生した。
test_my_package_core.py
import os
import traceback
import unittest
from my_package_name.my_package_core import PackageClass
class TestPage(unittest.TestCase):
(略)
if __name__ == '__main__':
unittest.main()
- テスト実行すると
ModuleNotFoundError
$ python tests/test_my_package_core.py
Traceback (most recent call last):
File "my_package_core.py", line 5, in <module>
from my_package_name.my_package_core import PackageClass
ModuleNotFoundError: No module named 'my_package_name'
- IDEで作成したProjectのパスはsys.pathに登録されるのでは?と確認してみる
(project) Macintosh:$ python
Python 3.5.2 (default, Dec 30 2016, 02:25:25)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.path)
['', '/Users/name/.pyenv/versions/3.5.2/lib/python35.zip', '/Users/name/.pyenv/versions/3.5.2/lib/python3.5', '/Users/name/.pyenv/versions/3.5.2/lib/python3.5/plat-darwin', '/Users/name/.pyenv/versions/3.5.2/lib/python3.5/lib-dynload', '/Users/name/project/lib/python3.5/site-packages', '/Users/name/project/lib/python3.5/site-packages/setuptools-39.1.0-py3.5.egg', '/Users/name/project/lib/python3.5/site-packages/pip-10.0.1-py3.5.egg']
Why?
Pycharmコンソールで実行する場合
/Users/name/project/bin/python "/Applications/PyCharm CE.app/Contents/helpers/pydev/pydevconsole.py" 51470 51471
import sys; print('Python %s on %s' % (sys.version, sys.platform))
sys.path.extend(['/Users/name/PycharmProjects/project'])
PyDev console: starting.
Python 3.5.2 (default, Dec 30 2016, 02:25:25)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
- Pythonコンソールの初期化処理で
sys.path.extend(['/Users/name/PycharmProjects/project'])
を実行してくれていることが分かった。
Pycharmのテスト機能で実行する場合
$ Launching unittests with arguments python -m unittest test_package_core.TestPage in /Users/name/PycharmProjects/project/tests
- なるほど -m など様々な点で配慮していただいているようで。
IDEサマサマ
- Pycharmが意識せず吸収してくれている点が多々ある
- Pythonコンソール起動時の作成したプロジェクトフォルダをsys.pathに追加する処理や
- Pycharmのユニットテスト実行機能が
python -m
を付与し、ModuleNotFoundを回避してくれている
とはいえ、分かったからにはterminalからだろうが同じ状態に出来ることを目指したい
testcase側でProjectパスを追加することで対応
test_my_package_core.py
import os
import sys
import traceback
import unittest
# 以下1lineを追加
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from my_package_name.my_package_core import PackageClass
class TestPage(unittest.TestCase):
(略)
if __name__ == '__main__':
unittest.main()
$ python tests/test_my_package_core.py
Success
パッケージ構成
-
再現可能なテストを実行するために、かつ公開パッケージとして適切なディレクトリ構造にするために作成するパッケージのディレクトリとテストディレクトリは分類し、以下のようなパッケージ構成にしています。
- 偉い人がそう言っている
- 世の素晴らしい公開パッケージの構成もそうなっているため
パッケージ構成
Macintosh:$ tree
Project
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── docs
│ ├── Makefile
│ ├── conf.py
│ ├── index.rst
│ └── make.bat
├── setup.py
├── my_package_name
│ ├── __init__.py
│ ├── my_package_core.py
│ └── my_package_helper.py
└── tests
├── __init__.py
├── test_my_package_helper.py
└── test_my_package_core.py
いざテスト
- testsディレクトリ配下のtest_my_package_core.pyのテストを実装(my_package_nameのmy_package_core.pyのあるクラスのみをimport)し、ターミナルからテスト実行しようとしたところ、エラーが発生した。
test_my_package_core.py
import os
import traceback
import unittest
from my_package_name.my_package_core import PackageClass
class TestPage(unittest.TestCase):
(略)
if __name__ == '__main__':
unittest.main()
- テスト実行すると
ModuleNotFoundError
$ python tests/test_my_package_core.py
Traceback (most recent call last):
File "my_package_core.py", line 5, in <module>
from my_package_name.my_package_core import PackageClass
ModuleNotFoundError: No module named 'my_package_name'
- IDEで作成したProjectのパスはsys.pathに登録されるのでは?と確認してみる
(project) Macintosh:$ python
Python 3.5.2 (default, Dec 30 2016, 02:25:25)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print(sys.path)
['', '/Users/name/.pyenv/versions/3.5.2/lib/python35.zip', '/Users/name/.pyenv/versions/3.5.2/lib/python3.5', '/Users/name/.pyenv/versions/3.5.2/lib/python3.5/plat-darwin', '/Users/name/.pyenv/versions/3.5.2/lib/python3.5/lib-dynload', '/Users/name/project/lib/python3.5/site-packages', '/Users/name/project/lib/python3.5/site-packages/setuptools-39.1.0-py3.5.egg', '/Users/name/project/lib/python3.5/site-packages/pip-10.0.1-py3.5.egg']
Why?
Pycharmコンソールで実行する場合
/Users/name/project/bin/python "/Applications/PyCharm CE.app/Contents/helpers/pydev/pydevconsole.py" 51470 51471
import sys; print('Python %s on %s' % (sys.version, sys.platform))
sys.path.extend(['/Users/name/PycharmProjects/project'])
PyDev console: starting.
Python 3.5.2 (default, Dec 30 2016, 02:25:25)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
- Pythonコンソールの初期化処理で
sys.path.extend(['/Users/name/PycharmProjects/project'])
を実行してくれていることが分かった。
Pycharmのテスト機能で実行する場合
$ Launching unittests with arguments python -m unittest test_package_core.TestPage in /Users/name/PycharmProjects/project/tests
- なるほど -m など様々な点で配慮していただいているようで。
IDEサマサマ
- Pycharmが意識せず吸収してくれている点が多々ある
- Pythonコンソール起動時の作成したプロジェクトフォルダをsys.pathに追加する処理や
- Pycharmのユニットテスト実行機能が
python -m
を付与し、ModuleNotFoundを回避してくれている
とはいえ、分かったからにはterminalからだろうが同じ状態に出来ることを目指したい
testcase側でProjectパスを追加することで対応
test_my_package_core.py
import os
import sys
import traceback
import unittest
# 以下1lineを追加
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from my_package_name.my_package_core import PackageClass
class TestPage(unittest.TestCase):
(略)
if __name__ == '__main__':
unittest.main()
$ python tests/test_my_package_core.py
Success
python -m
を付与し、ModuleNotFoundを回避してくれているtestcase側でProjectパスを追加することで対応
test_my_package_core.py
import os
import sys
import traceback
import unittest
# 以下1lineを追加
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from my_package_name.my_package_core import PackageClass
class TestPage(unittest.TestCase):
(略)
if __name__ == '__main__':
unittest.main()
$ python tests/test_my_package_core.py
Success
以上
Author And Source
この問題について(Python自作パッケージのunittestをそれっぽいディレクトリ構成で実施する際にModuleNotFoundErrorを回避する), 我々は、より多くの情報をここで見つけました https://qiita.com/reinhardhq/items/838df0bf09611f3c5872著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .