"cannot import name '__main__" 尝试从我创建的 wheel 中 运行 控制台命令时

"cannot import name '__main__" when trying to run a console command from the wheel I created

几个月来,我一直在努力为我的项目 'rpn' (https://github.com/ConceptJunkie/rpn) 创建一个轮子。我终于到了一切似乎都正确的地步,除了我试图在 setup.py.

中创建的控制台命令

我的项目有多个模块,我的 setup.py,我将在下面 post 创建一个似乎可以正常工作的轮子。我没有对 virtualenv 进行过广泛的测试,因为我想掌握正确的基础知识。当我用 pip 安装轮子时,我可以 运行 python 并导入我的模块和 运行 功能就好了。但是,我将该项目(和一些辅助实用程序)用作命令行应用程序,所以我希望轮子为我创建一些命令。

我在 Windows,所以当我安装轮子时,它会为我定义的每个命令创建一个小的 EXE 文件,但是当我尝试 运行 这些命令时,我得到这个输出:

c:\app\python36\scripts>makeRPNUnits.exe
Traceback (most recent call last):
  File "c:\app\python36\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\app\python36\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\app\python36\Scripts\makeUnits.exe\__main__.py", line 5, in <module>
ImportError: cannot import name '__main__'

这是我 运行 准备应用程序使用的数据文件所需的小实用程序之一,但其他命令都做同样的事情。如果我进入 Python 我可以 运行 就好了:

c:\app\python36\scripts>python
Python 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit 
(AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.

>>> import makeUnits
>>> makeUnits.main( )
makeUnits 6.999.13 (7.0b13) - RPN command-line calculator unit conversion data generator 
copyright (c) 2018 (1988), Rick Gutleber (rickg@his.com)

[and so on...]

实际应用我也可以通过Python:

>>> rpn.handleOutput( rpn.rpn( [ '2', '2', '+' ] ) )
4

我花了几个小时寻求帮助...设置工具的文档非常稀疏和不透明,我只能在其他地方找到很小的信息片段,而且经常相互矛盾。了解细节永远不够,但足以做出有根据的猜测。

我的目录结构如下所示:

rpn/
├── rpndata
│   ├── various_text_files
└── setup.py
    __init__.py
    rpn.py
    makeUnits.py
    makeHelp.py
    MANIFEST.in
    rpn*.py

我希望创建的每个命令都在处理 main 的单独模块中表示。例如:

if __name__ == '__main__':
    main( )

其余文件实现了程序和各种实用程序使用的所有功能。同样,大部分内容都在 Python 中运行,但 /python36/scripts 中的命令失败并出现上述错误。

整个setup.py比较冗长,但相关的部分是这样的:

entry_points = {
    'console_scripts': [
        'rpn = rpn.rpn:__main__',
        'makeRPNHelp = rpn.makeHelp:__main__',
        'makeRPNUnits = rpn.makeUnits:__main__',
        'prepareRPNPrimeData = rpn.preparePrimeData:__main__',
        'testRPN = rpn.testRPN:__main__',
    ],
}

专家们可能需要更多细节,我很乐意提供,当然,整个项目都可以从 GitHub 获得。该项目相当大,而且有些复杂...它超出了我管理 Python 项目的能力,因为这对我来说只是一种爱好,我在日常工作中使用 C++。

感谢您的帮助。

对于您项目的特殊情况,您已经在每个模块中定义了一个 main 函数,因此要使 setuptool 入口点起作用,您可以直接直接引用它们,就好像您如何导入它们一样 (import makeUnits ), 即:

entry_points = {
    'console_scripts': [
        'rpn = rpn:main',
        'makeRPNHelp = makeHelp:main',
        'makeRPNUnits = makeUnits:main',
        ...
    ]
}

为什么您可能对需要 __main__ 感到困惑是因为这个值被分配给模块 __name__ 如果那是可执行脚本,或者将模块命名为 __main__.py is a way to allow a module to be executed directly using python -m example.module on the command line (pip for instance does this python -m pip 工作)。但是,对于标准入口点,只需直接引用 module:attr,它就会达到您的预期。


此外,请考虑通过 documentation about packaging on the Python site, as it will address how to properly package a project (which setuptools documentation don't actually teach, as you noted). Also consider creating a directory called rpn and putting your rpn*.py files into there to better organize your program code into its own namespace (so to make the entry point as you originally defined it (e.g. makeHelp = rpn.makeHelp:main work; naturally this comes with a change in how the main function is imported (from rpn.makeHelp import main)), though I note that on PyPI (the official python package index) that there is already a rpn package