14

我有一个小型 python 应用程序,我想将它制作成类似 UNIX 系统的可下载/可安装的可执行文件。我的印象是 setuptools 将是实现这一目标的最佳方式,但不知何故,这似乎不是一项常见的任务。

我的目录结构如下所示:

myappname/
|-- setup.py
|-- myappname/
|   |-- __init__.py
|   |-- myappname.py
|   |-- src/
|      |-- __init__.py
|      |-- mainclassfile.py
|      |-- morepython/
|         |-- __init__.py
|         |-- extrapython1.py
|         |-- extrapython2.py

包含的文件if __name__ == "__main__": 是 myappname.py。该文件在顶部有一行,import src.mainclassfile.

下载后,我希望用户能够执行以下操作

$ python setup.py build
$ python setup.py install

然后它将是一个已安装的可执行文件,他们可以从命令行的任何位置调用它:

$ myappname arg1 arg2

我的 setup.py 的重要部分如下:

from setuptools import setup, find_packages
setup(
  name='code2flow',
  scripts=['myappname/myappname.py'],
  package_dir={'myappname': 'myappname'},
  packages=find_packages(),
  )

当前状态

通过运行:

$ sudo python setup.py install

然后在一个新的外壳中:

$ myapp.py

我收到一个No module named错误

4

1 回答 1

19

这里的问题是你的包布局被破坏了。

它恰好在原地工作,至少在 2.x 中。为什么?您访问的包不是myappname——但是与该包目录相同的目录也是顶级脚本目录,因此您最终会通过旧式相对导入获得它的任何兄弟姐妹。

当然,一旦你安装了东西,你最终会myappname在你的站点包中安装包,然后myappname.py在你的 PATH 的某个地方安装一个副本,所以相对导入不可能工作。

正确的做法是将顶级脚本放在包之外(或者,理想情况下,放在bin目录中)。

此外,您的模块和脚本不应具有相同的名称。(有一些方法可以使这项工作发挥作用,但是……只是不要尝试。)

因此,例如:

myappname/
|-- setup.py
|-- myscriptname.py
|-- myappname/
|   |-- __init__.py
|   |-- src/
|      |-- __init__.py
|      |-- mainclassfile.py

当然,到目前为止,它所做的一切都是在原地模式下中断,就像它在安装时中断一样。但至少这让事情更容易调试,对吧?

无论如何,您myscriptname.py必须使用绝对导入:

import myappname.src.mainclassfile

setup.py必须在正确的位置找到脚本:

scripts=['myscriptname.py'],

最后,如果您需要myscriptname.py在模块内部和脚本中访问一些代码,正确的做法是将其重构为两个文件——但如果由于某种原因这太难了,您总是可以编写一个包装器脚本。

有关更多详细信息,请参阅Hitchhiker 打包指南中的安排文件和目录结构以及相关部分。

另请参阅PEP 328了解有关绝对导入和相对导入的详细信息(但请记住,当它提到“最高 Python 2.5”时,它实际上意味着“最高 2.7”,而“从 2.6 开始”表示“从 3.0 开始”。

setup.py有关包含通过(并且,通常是easy_install和)以这种方式安装的脚本的包的一些示例pip,请参见ipythonbpythonmodulegraphpy2app,当然easy_install还有pip它们本身。

于 2013-05-24T23:58:58.270 回答