【问题标题】:Building a Python CLI: how to properly declare entry_points in setuptools.setup构建 Python CLI:如何在 setuptools.setup 中正确声明 entry_points
【发布时间】:2019-10-17 07:04:54
【问题描述】:

解决了使用click 而不是argparse,见下文..


我正在构建一个 Python 命令行工具,遵循 Thomas Stringer 的 great tutorial。我的工具是这样使用的

$ python rsearch.py -f foo -b bar

我将它与python setup.py sdistpython setup.py install 打包在一起,遵循docs。一切都很好!现在我将它安装为带有pip install -e .pip 包,我的可执行文件已准备就绪,对吧?

$ which rsearch
/usr/local/bin/rsearch

问题是

$ rsearch -f foo.txt -b bar/
Searching...
Searching... 
Found item from foo.txt in bar/path/to/target/
Searching...
Done!
Traceback (most recent call last):
  File "/usr/local/bin/rsearch", line 11, in <module>
    load_entry_point('rsearch', 'console_scripts', 'rsearch')()
TypeError: main() missing 2 required positional arguments: 'foo' and 'bar'

我认为问题在于我的entry_points 没有正确指定:

from setuptools import setup
setup(
    name='rsearch',
    version='0.0.1',
    py_modules=['rsearch'],
    entry_points={
        'console_scripts': [
            'rsearch=rsearch.__main__:main'
        ]
    })

根据我遵循的教程,尽管从 2016 年开始,这应该可以工作。我知道 distutils.core 是 Python 3.7 的首选 setup() 方法,但我也尝试过,并且得到了相同的结果。

我的__main__.py 看起来像这样:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument(
    '--foo', 
    '-f',
    help='Foo help'
)
parser.add_argument(
    '--bar', 
    '-b', 
    help='Bar help'
)

def main(foi, gras):
    # code code code 
    # code code..

args = parser.parse_args()
main(args.foo, args.bar)

我的项目结构如下:

rsearch
├── rsearch
│   └── __main__.py
└── setup.py

旁注:python 脚本按预期运行:

$ python __main__.py -f foo.txt -b bar/
Searching...
Searching... 
Found item from foo.txt in bar/path/to/target/
Searching...
Done!

所以脚本似乎不是问题。仅在尝试运行使用 setuptools.setup() 构建的可执行文件时才会出现此错误

我正在使用Python 3.7.3pip 19.1.1。非常感谢任何帮助或建议。


更新

根据@techouse 的建议,我使用click 复制了这个CLI,并按照他们的documentation 更新了setup.py 中的entry_points 分配。

setup(
    name='rsearch',
    version='0.0.1',
    py_modules=['rsearch'],
    install_requires=[
        'Click',
    ],
    entry_points='''
        [console_scripts]
        rsearch=rsearch_script:cli
    ''',
)

我将脚本重命名为 rsearch_script.py 并使用 click 装饰器而不是 argparse

@click.command()
@click.option('--foo', '-f')
@click.option('--bar', '-b')
def cli(foo, bar):
    # code code code
    # code code..

我还移动了文件(来自文档,可能无关紧要)

rsearch
├── rsearch_script.py
└── setup.py

在这个目录中,我运行了pip install -e .,然后砰!

没有错误,工作正常。

$ rsearch -f foo.txt -b bar
Searching...
Searching...

# ...

$ rsearch --help
Usage: rsearch [OPTIONS]

Options:
  -f, --foo TEXT
  -b, --bar TEXT
  --help

感谢@techouse 的提示 - 你说得对,从现在开始我将使用 Click 来开发 Python CLI。

【问题讨论】:

  • 我建议使用Click 而不是argparse,仅仅是因为测试argparse 很痛苦。然后检查docs here
  • 感谢@techouse 的建议,我听说过有关 Click 的积极消息,它是一个可爱的图书馆。我会试一试!

标签: python python-3.x command-line-interface setuptools setup.py


【解决方案1】:

entry_points 将直接调用您的main - 这是'rsearch=rsearch.__main__:main' 中的最后一个main。这意味着调用 args 解析器的代码永远不会被调用,因此您会遇到参数永远不会被传入的问题。

不要更改您的setup.py,而是将args = parser.parse_args() 行移到main 函数下。

if __name__ == __main__ 条件只会在脚本像您所做的那样直接运行时运行,而不是在几乎任何其他情况下。

【讨论】:

  • 您好 dwagon,感谢您的洞察力!确实从我的脚本中删除 if __name__ 等确实修复了错误,并且 rsearch --help 现在运行没有问题。奇怪的是,当我使用真实参数rsearch -f foo.txt -b bar/ 运行脚本时,我仍然会遇到这个确切的错误。奇怪的是,这真的有效吗,但是一旦脚本完成运行,它就会产生与上面完全相同的Traceback..
  • 我根据@dwagon 的回复更新了这篇文章。再次感谢!为什么我们需要为可执行文件删除 if __name__ == "__main__" 很有意义,我只是习惯于为 Python 脚本执行此操作,以至于我什至没有三思而后行。
猜你喜欢
  • 2012-05-07
  • 2012-03-02
  • 2017-12-16
  • 2015-02-14
  • 1970-01-01
  • 2021-12-06
  • 2015-02-24
  • 2021-06-06
  • 1970-01-01
相关资源
最近更新 更多