How do you organise a python project that contains multiple packages so that each file in a package can still be run individually?
DR
下面是一个示例存储库,它的设置如第一个图表(如下)中所述:https://github.com/poddster/package_problems
如果你能让它看起来像第二张关于项目组织的图表,并且仍然可以运行以下命令,那么你已经回答了这个问题:
1 2 3 4 5 6 7 8 9 10 11 | $ 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和
目前我有这个目录结构,一切正常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | . ├── 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 ├── ... |
一些脚本来自其他脚本的
"my-tool.py"是利用所有其他脚本的主脚本。
我想发生的事不过,我想改变项目的组织方式。项目本身代表了一个可供用户使用的完整程序,并将按此方式分发,但我知道它的某些部分在以后的不同项目中会很有用,因此我希望尝试将当前文件封装到一个包中。在不久的将来,我还将向同一个项目添加其他包。
为了促进这一点,我决定将该项目重新组织成如下形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | . ├── 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 ├── ... |
但是,我无法确定一个符合以下标准的项目组织:
主要问题是包和测试使用的导入语句。
目前,所有的软件包,包括测试,都只是简单地执行
请注意,支持PY2.7是一项要求,因此所有文件的顶部都有
如果按上面所示移动文件,但保留所有导入语句的当前状态:
二
如果我随后将测试脚本更改为:
1 | from my_tool import x |
三
如果我保持相同的结构,将所有内容都改为
例如1。
1 2 3 4 | Traceback (most recent call last): File"./my_tool/a.py", line 34, in <module> from my_tool import b ImportError: cannot import name b |
四
我也尝试过
我不能只用
a)我没有main.py。我想我可以要一个吗?b)我希望能够运行所有脚本,而不仅仅是主脚本?
我试过了:
1 2 3 | if __name__ == '__main__' and __package__ is None: from os import sys, path sys.path.append(path.dirname(path.dirname(path.abspath(__file__)))) |
但没用。
我也尝试过:
1 2 | __package__ ="my_tool" from . import b |
但收到:
1 | SystemError: Parent module 'loading_tool' not loaded, cannot perform relative import |
在
什么是正确的魔法咒语和目录布局,使所有这些工作?
一旦移动到所需的配置,用于加载特定于
创建
创建
在
1 2 3 4 | import c import d import k import s |
到:
1 2 3 4 | from . import c from . import d from . import k from . import s |
您需要对所有其他文件进行类似的更改。(您提到曾尝试设置
在
1 | import my_tool |
到:
1 | from .. import my_tool |
与所有其他测试文件类似。
通过上面的修改,我可以直接运行模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $ 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| |
我可以运行测试:
1 2 3 4 5 6 | $ nosetests ........ ---------------------------------------------------------------------- Ran 8 tests in 0.006s OK |
请注意,我可以用python 2.7和python 3来运行上面的代码。
我建议使用适当的
修改可以从命令行调用的模块,以
1 2 3 | if __name__ =="__main__": print("my_tool main!") print(do_something()) |
你有:
1 2 3 4 5 6 | def main(): print("my_tool main!") print(do_something()) if __name__ =="__main__": main() |
创建一个包含正确的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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=[ ], ) |
上面的文件指示
第1点
我相信这是可行的,所以我不作评论。
第2点我总是使用与我的测试工具处于同一级别的测试,而不是低于它,但是如果在每个测试文件的顶部执行此操作(在将我的测试工具或任何其他py文件导入到同一目录之前),它们应该可以工作。
1 2 3 4 | import os import sys sys.path.insert(0, os.path.abspath(__file__).rsplit(os.sep, 2)[0]) |
第3点
在my_second_package.py中,在顶部执行此操作(在导入我的_工具之前)
1 2 3 4 5 6 | import os import sys sys.path.insert(0, os.path.abspath(__file__).rsplit(os.sep, 2)[0] + os.sep + 'my_tool') |
最好的问候,
JM
要从命令行运行它,并像库一样运行它,同时允许nosetest以标准方式运行,我相信您必须对导入进行双重处理。
例如,python文件需要:
1 2 3 4 | try: import f except ImportError: import tools.f as f |
我在Github上做了一个公关,你链接到所有的测试用例。
https://github.com/poddster/package_问题/pull/1
编辑:忘记了
1 2 | import tools tools.c.do_something() |