TL; DR
以下は、最初の図(下記)で説明されているように設定されたリポジトリの例です。 https://github.com/Poddster/package_problems
プロジェクト構成の点で2番目の図のように見せても、次のコマンドを実行できる場合は、質問に回答しています。
$ git clone https://github.com/Poddster/package_problems.git
$ cd package_problems
<do your magic here>
$ nosetests
$ ./my_tool/my_tool.py
$ ./my_tool/t.py
$ ./my_tool/d.py
(or for the above commands, $ cd ./my_tool/ && ./my_tool.py is also acceptable)
または、関連するファイル(「パッケージ」)をグループ化し、すべてのファイルを個別に実行し、同じパッケージ内の他のファイルにファイルをインポートし、パッケージ/ファイルを他のパッケージのファイルにインポートできるようにする別のプロジェクト構造を教えてください。
私はpythonファイルの束を持っています。それらのほとんどは、コマンドラインから呼び出すことができるときに便利です。つまり、すべてがargparseおよびif __name__ == "__main__"
を使用して便利なことを行います。
現在、私はこのディレクトリ構造を持っており、すべてが正常に動作しています:
.
├── config.txt
├── docs/
│ ├── ...
├── my_tool.py
├── a.py
├── b.py
├── c.py
├── d.py
├── e.py
├── README.md
├── tests
│ ├── __init__.py
│ ├── a.py
│ ├── b.py
│ ├── c.py
│ ├── d.py
│ └── e.py
└── resources
├── ...
一部のスクリプトは、他のスクリプトからのimport
処理を実行するためのものです。しかし、スクリプトは単なるライブラリではなく、すべて呼び出し可能です。例えば./my_tool.py
、./a.by
、./b.py
、./c.py
などを呼び出すことができ、それらはユーザーにとって有用なことを行います。
「my_tool.py」は、他のすべてのスクリプトを活用するメインスクリプトです。
ただし、プロジェクトの編成方法を変更したいと思います。プロジェクト自体は、ユーザーが使用できるプログラム全体を表しており、そのように配布されますが、その一部は後で別のプロジェクトでも役立つため、現在のファイルをパッケージにカプセル化したいと思います。近い将来、この同じプロジェクトに他のパッケージも追加する予定です。
これを容易にするために、プロジェクトを次のようなものに再編成することにしました。
.
├── config.txt
├── docs/
│ ├── ...
├── my_tool
│ ├── __init__.py
│ ├── my_tool.py
│ ├── a.py
│ ├── b.py
│ ├── c.py
│ ├── d.py
│ ├── e.py
│ └── tests
│ ├── __init__.py
│ ├── a.py
│ ├── b.py
│ ├── c.py
│ ├── d.py
│ └── e.py
├── package2
│ ├── __init__.py
│ ├── my_second_package.py
| ├── ...
├── README.md
└── resources
├── ...
ただし、次の基準を満たすプロジェクト組織はわかりません。
my_tool\a.py
またはcd my_tool && a.py
として)import my_tool
を実行できます主な問題は、パッケージとテストで使用されるインポート文にあります。
現在、テストを含むすべてのパッケージは単にimport <module>
を実行するだけで正しく解決されます。しかし、周りの物事を揺さぶるときはそれは機能しません。
Py2.7のサポートは必須であるため、すべてのファイルの先頭にfrom __future__ import absolute_import, ...
があることに注意してください。
上記のようにファイルを移動しますが、現在のようにすべてのインポートステートメントをそのままにします。
$ ./my_tool/*.py
は機能し、すべて正しく動作します$ nosetests
最上位ディレクトリから実行しても機能しません。テストはパッケージスクリプトのインポートに失敗します。次に、テストスクリプトを次のように変更した場合:
from my_tool import x
$ ./my_tool/*.py
は引き続き機能し、すべて正しく動作します$ nosetests
最上位ディレクトリから実行しても機能しません。その後、テストは正しいスクリプトをインポートできますが、テストスクリプトがそれらをインポートすると、スクリプト自体のインポートは失敗します。同じ構造を維持し、everythingをfrom my_tool import
に変更すると、次のようになります。
$ ./my_tool/*.py
結果はImportError
sになります$ nosetests
はすべて正常に実行されます。例えばの1 .:
Traceback (most recent call last):
File "./my_tool/a.py", line 34, in <module>
from my_tool import b
ImportError: cannot import name b
私はfrom . import x
も試しましたが、スクリプトを直接実行するためにValueError: Attempted relative import in non-package
で終わるだけです。
python -m pkg.tests.core_test
をそのまま使用することはできません
a)main。pyがありません。私は持っていると思いますか?
b)メインだけでなく、すべてのスクリプトを実行できるようにしたいですか?
私はもう試した:
if __name__ == '__main__' and __package__ is None:
from os import sys, path
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
しかし、それは助けにはなりませんでした。
私も試しました:
__package__ = "my_tool"
from . import b
しかし受け取った:
SystemError: Parent module 'loading_tool' not loaded, cannot perform relative import
import my_tool
の前にfrom . import b
を追加すると、ImportError: cannot import name b
で終了します
このすべてを機能させるための魔法の呪文とディレクトリレイアウトの正しいセットは何ですか?
目的の構成に移動すると、my_tool
に固有のモジュールをロードするために使用している絶対インポートは機能しなくなります。
my_tool
サブディレクトリを作成してファイルをそこに移動した後、3つの変更が必要です。
my_tool/__init__.py
を作成します。 (あなたはすでにこれをしているようですが、私は完全性のためにそれを述べたかったです。)
my_tool
の直下のファイル:import
ステートメントを変更して、現在のパッケージからモジュールをロードします。したがって、my_tool.py
の変更:
import c
import d
import k
import s
に:
from . import c
from . import d
from . import k
from . import s
他のすべてのファイルにも同様の変更を加える必要があります。 (__package__
を設定してから相対インポートを実行したが、__package__
の設定は不要であると述べました。)
my_tool/tests
にあるファイルで、テストするコードをインポートするimport
ステートメントを、階層内の1つのパッケージからロードする相対インポートに変更します。したがって、test_my_tool.py
の変更:
import my_tool
に:
from .. import my_tool
他のすべてのテストファイルについても同様です。
上記の変更により、モジュールを直接実行できます。
$ python -m my_tool.my_tool
C!
D!
F!
V!
K!
T!
S!
my_tool!
my_tool main!
|main tool!||detected||tar edit!||installed||keys||LOL||ssl connect||parse ASN.1||config|
$ python -m my_tool.k
F!
V!
K!
K main!
|keys||LOL||ssl connect||parse ASN.1|
テストを実行できます:
$ nosetests
........
----------------------------------------------------------------------
Ran 8 tests in 0.006s
OK
Python 2.7 and Python 3。
my_tool
の下のさまざまなモジュールを直接実行可能にするのではなく、適切なsetup.py
ファイルを使用してエントリポイントを宣言し、パッケージのインストール時にこれらのエントリポイントをsetup.py
で作成することをお勧めします。このコードを配布するつもりなので、とにかく正式にパッケージ化するにはsetup.py
を使用する必要があります。
コマンドラインから呼び出すことができるモジュールを変更して、以下の代わりにmy_tool/my_tool.py
を例にとります。
if __name__ == "__main__":
print("my_tool main!")
print(do_something())
あなたが持っている:
def main():
print("my_tool main!")
print(do_something())
if __name__ == "__main__":
main()
適切なsetup.py
を含むentry_points
ファイルを作成します。例えば:
from setuptools import setup, find_packages
setup(
name="my_tool",
version="0.1.0",
packages=find_packages(),
entry_points={
'console_scripts': [
'my_tool = my_tool.my_tool:main'
],
},
author="",
author_email="",
description="Does stuff.",
license="MIT",
keywords=[],
url="",
classifiers=[
],
)
上記のファイルは、モジュールsetup.py
のmain
メソッドを呼び出すmy_tool
という名前のスクリプトを作成するようmy_tool.my_tool
に指示しています。私のシステムでは、パッケージがインストールされると、/usr/local/bin/my_tool
にあるスクリプトがあり、my_tool.my_tool
のmain
メソッドを呼び出します。上で示したpython -m my_tool.my_tool
の実行と同じ出力が生成されます。
動作していると思うので、コメントしません。
私は常にmy_toolと同じレベルでテストを使用しましたが、その下ではありませんが、これを各テストファイルの最上部で実行すると機能します(my_toolまたは他のpyファイルを同じディレクトリにインポートする前に)
import os
import sys
sys.path.insert(0, os.path.abspath(__file__).rsplit(os.sep, 2)[0])
My_second_package.pyの上部でこれを行います(my_toolをインポートする前に)
import os
import sys
sys.path.insert(0,
os.path.abspath(__file__).rsplit(os.sep, 2)[0] + os.sep
+ 'my_tool')
宜しくお願いします、
JM
Nosetestを標準的な方法で操作できるようにしながら、コマンドラインから実行し、ライブラリのように動作させるには、インポートでダブルアップアプローチを実行する必要があると思います。
たとえば、Pythonファイルには以下が必要です。
try:
import f
except ImportError:
import tools.f as f
すべてのテストケースが機能しているgithubからPRを実行しました。
https://github.com/Poddster/package_problems/pull/1
編集:__init__.py
のインポートを忘れて、他のパッケージで適切に使用できるようになり、追加されました。これで、次のことができるはずです。
import tools
tools.c.do_something()