包的结构也可以是 运行 作为命令行脚本

Structure of package that can also be run as command line script

我用the 'standard' minimal structure写了一个包。它看起来像这样:

my_package/
    my_package/
        __init__.py
    setup.py

__init__.py 包含一个 class,因此可以像预期的那样简单地导入和使用。

但是,该代码确实适合以命令行方式使用,例如

python my_package --arg1 "I like bananas."

起初,我只是在 __init__ 中进行了 if __name__ == '__main__' 检查,然后使用 argparse。这个 有效 但它并不漂亮,因为这意味着你会像这样从命令行调用它:

python my_package/__init__.py --arg1 "I like bananas."

根据我的阅读,这是 __main__.py 文件的来源,该文件将作为文件夹内的默认脚本执行(类似于网站上的 index.html 文件)。我的想法是然后简单地导入 __init__.py、运行 argparse 并将参数提供给 class 构造函数。像这样:

import argparse
from __init__ import MyClass

parser = argparse.ArgumentParser()
parser.add_argument("--arg1", help="Some dummy value")

args = parser.parse_args()
my_class = MyClass(**vars(args))
my_class.do_stuff()

类似的包应该是这样构建的,还是有更好的方法?


上面的作品 但是 PyCharm 告诉我 __main__.py __init__ 是一个未解决的引用。该导入行上的 MyClass 相同。当我使用 .__init__ 代替(带点)时,警告消失但代码不再工作,给我一个 ImportError: attempted relative import with no known parent package.

我想向您推荐一个不同的结构:

my_package/
    my_package/
        __init__.py
        cli_scripts.py
    setup.py

让我们假设您的 __init__.py 看起来像这样(作为旁注,我建议将其中定义的 类 移动到一个单独的文件,然后只需将该文件导入 __init__):

class Test(object):

    def __init__(self, a)
        self.a = a

    def __call__(self):
        print(self.a)

现在包中有一个额外的文件,它利用了您在包中实现的东西,我们称之为 cli_scripts.py:

import argparse

from my_package import Test


def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("a", type=int, help="Just an example argument")
    return parser.parse_args()

def demonstration():
    args = parse_args()
    t = Test(a=args.a)
    t()

我现在的建议是利用 setup.py 中的 console_scripts entry point,现在看起来像这样:

from setuptools import setup

setup(
    name='testpy',
    version='0.1',
    description='Just an example',
    author='RunOrVeith',
    author_email='xyz@mailinator.com',
    packages=["my_package"],
    entry_points={
        "console_scripts": ["mypkg=my_package.cli_scripts:demonstration"]},
    install_requires=[],
)

现在,当您 运行 pip install . 在顶级 my_package 文件夹中时,您的软件包将被安装。 entry_points 自动为您生成一个可执行文件,您现在可以使用您在 setup.py 中给它的名称调用它,在本例中为 mypkg。这意味着您现在可以 运行

mypkg 5 这将调用 demonstration()

这样:

  • 您不需要处理任何 __main__ 个文件
  • 你可以给你的脚本一个有意义的名字
  • 你不需要关心你的脚本是否可执行,或者用python
  • 指定运行脚本
  • 您可以在列表中添加任意数量的这些内容entry_points
  • 它迫使你编写你也可以从其他模块调用的函数, 而不是 __main__

我觉得这很干净。 您可以在 this blog 中阅读更多关于 entry_points 的信息,它有更多的功能! 如果您想使用 __main__ 中指定的代码而不是这种 cli_scripts 方法,您可以查看 .