在标准项目目录结构中调用脚本(bin子目录的Python路径)

Art*_*cca 7 python setup.py

我正在尝试将我的Python代码放入用于部署的标准目录结构中setup.py,也许是PyPI.对于一个名为mylib的Python库,它将是这样的:

mylibsrc/
  README.rst
  setup.py
  bin/
    some_script.py
  mylib/
    __init.py__
    foo.py
Run Code Online (Sandbox Code Playgroud)

通常还有一个test/子目录,但我还没有尝试编写单元测试.bin/可以在官方Python打包文档中找到在子目录中包含脚本的建议.

当然,脚本的开头代码如下所示:

#!/usr/bin/env python
from mylib.foo import something
something("bar")
Run Code Online (Sandbox Code Playgroud)

当它最终部署脚本(例如devpi)然后用pip安装它时,这很有效.但是,如果我直接从源目录运行脚本,就像我在开发库/脚本的新更改时一样,我收到此错误:

ImportError: No module named 'mylib'
Run Code Online (Sandbox Code Playgroud)

即使当前工作目录是root mylibsrc/并且我通过键入运行脚本也是如此./bin/some_script.py.这是因为Python开始在正在运行的脚本(即from bin/)的目录中搜索包,而不是当前的工作目录.

什么是一种好的,永久性的方法,可以在开发包时轻松运行脚本?

这是一个相关的其他问题(特别是对第一个答案的评论).

到目前为止,我发现的解决方案分为三类,但没有一个是理想的:

  1. 在运行脚本之前,以某种方式手动修复Python的模块搜索路径.
    • 您可以手动添加mylibsrc到我的PYTHONPATH环境变量.这似乎是最官方的(Pythonic?)解决方案,但意味着每次我检查一个项目时我都要记得在我可以运行其中的任何代码之前手动更改我的环境.
    • 添加.到我的PYTHONPATH环境变量的开头.据我了解,这可能会有一些安全问题.如果我是唯一一个使用我的代码的人,这实际上是我最喜欢的技巧,但我不是,我不想让别人这样做.
    • 在互联网上查看答案时,对于目录中的文件,test/我看到了他们所有(间接)包含一行代码的建议sys.path.insert(0, os.path.abspath('..'))(例如,在构建项目时).呸!对于仅用于测试的文件而言,这似乎是一个可以忍受的黑客攻击,而不是那些随软件包一起安装的文件.
    • 编辑:我已经找到了另一种选择,结果就是这个类别:通过使用Python的-m脚本运行脚本,搜索路径在工作目录而不是bin/目录中开始.有关详细信息,请参阅下面的答案.
  2. 在使用之前,使用setup.py(直接运行或使用pip)将软件包安装到虚拟环境中.
    • 如果我只是测试一个我不确定在语法上是否正确的变化,这似乎有些过分.我正在研究的一些项目甚至不打算作为软件包安装,但是我想对所有内容使用相同的目录结构,这意味着编写一个setup.py只是为了测试它们!
    • 编辑:下面的答案中讨论了两个有趣的变体:setup.py developlogc答案和pip install -e我的命令.他们避免为每一个小编辑重新"安装",但你仍然需要创建一个setup.py你永远不打算完全安装的包,并且与PyCharm(它有一个菜单项来运行develop命令但不能很好地工作)没有简单的方法来运行它复制到虚拟环境的脚本.
  3. 将脚本移动到项目的根目录(即mylibsrc/代替mylibsrc/bin/).
    • 呸!这是最后的手段,但不幸的是,这似乎是目前唯一可行的选择.

Art*_*cca 4

作为脚本运行模块

自从我发布这个问题以来,我了解到您可以使用 Python 的-m命令行开关(我原以为只适用于包)来运行模块,就好像它是脚本一样。

所以我认为最好的解决方案是这样的:

  • 不要在 bin 子目录中编写包装器脚本,而是将大部分逻辑放入模块中(无论如何你都应该这样做),然后将其放在相关模块的末尾if __name__ == "__main__": main(),就像在脚本中一样。
  • 要在命令行上运行脚本,请直接调用模块,如下所示:python -m pkg_name.module_name
  • 如果您有 setup.py,正如 Alik 所说,您可以在安装时生成包装脚本,这样您的用户就不需要以这种有趣的方式运行它们。

PyCharm 不支持以这种方式运行模块(请参阅此请求)。但是,您可以像平常一样运行模块(以及 bin 中的脚本),因为 PyCharm 会自动将项目根添加到 PYTHONPATH,因此导入语句无需任何进一步的努力即可解析。不过,这有一些问题:

  • 主要问题是工作目录不正确,因此无法打开数据文件。不幸的是,没有快速解决办法。第一次运行每个脚本时,您必须停止它并更改其配置的工作目录(请参阅此链接)。
  • 如果您的包目录不直接位于项目根目录中,则需要在项目结构设置页面中将其父目录标记为源目录。
  • 相对导入不起作用,即你可以做from pkg_name.other_module import fn但不能做from .other_module import fn。无论如何,相对导入通常都是糟糕的风格,但它们对于单元测试很有用。
  • 如果一个模块具有循环依赖并且您直接运行它,那么它将最终被导入两次(一次为 as pkg_name.module_name,一次为 as __main__)。但无论如何你不应该有循环依赖。

额外的命令行乐趣:

  • 如果你仍然想放入一些脚本,bin/你可以使用它们来调用它们python -m bin.scriptname(但在Python 2中你需要将一个放在__init__.pybin目录中)。
  • 您甚至可以运行整个包,如果它有一个__main__.py,如下所示:python -m pkg_name

点可编辑模式

命令行还有一个替代方案,它并不那么简单,但仍然值得了解:

  • 使用 pip 的可编辑模式,记录在此处
  • 要使用它,请创建一个 setup.py,然后使用以下命令将该包安装到您的虚拟环境中:pip install -e .
  • 请注意尾随点,它指的是当前目录。
  • 这将从 setup.py 生成的脚本放在虚拟环境的 bin 目录中,并链接到包源代码,以便您可以编辑和调试它,而无需重新运行 pip。
  • 完成后,您可以运行pip uninstall pkg_name
  • setup.py这与的命令类似develop,但卸载似乎效果更好。