在 Python 中使用入口点时,区分导入的包和在 CLI 上运行的包

如何解决在 Python 中使用入口点时,区分导入的包和在 CLI 上运行的包

我有一个最常用作 CLI 工具的 python 包,但有时我会出于自己的目的将它作为库运行。 (例如,将其转换为网络应用或用于单元测试)

例如,当用作 CLI 命令时,我想 sys.exit(1)错误时向最终用户提供有关异常的很好的错误消息,但是 raise 在用作 CLI 命令时为异常导入的库。

我在 entry_points 中使用了 setup.py

entry_points={
        'console_scripts': [
            'jello=jello.cli:main'
        ]
    }

这很好用,但我无法轻易区分包是在 CLI 中运行还是导入,因为 __name__ 始终是 jello.cli。这是因为入口点基本上是照常导入包。

我尝试创建一个 __main__.py 文件并将我的入口点指向那里,但这似乎没有什么区别。

我正在考虑检查 sys.argv[0] 以查看我的程序名称是否存在,但这似乎是一个脆弱的黑客。 (万一用户给命令或其他东西起了别名)还有其他想法还是我做错了?目前,我一直在向我的函数传递一个 as_lib 参数,因此它们的行为会根据它们是作为模块加载还是从 CLI 运行而有所不同,但我想摆脱这种情况。

解决方法

我相信你想要的在 Python 中被称为“主守卫”。该模块在任何给定时间都知道其名称。如果模块是从 CLI 执行的,则其名称为 __main__。因此,您可以做的是将您想要的所有功能放在模块级别的某个函数中,然后您可以在“主守卫”中以不同的方式调用它,而不是将其称为库。这是“主卫”的样子:

def myfunc(thing,stuff):
   if not stuff:
      raise MyExc

if __name__ == '__main__':
    try:
       myfunc(x,y)
    except MyExc:
       sys.exit(1)

这里,只有在模块作为脚本从命令行执行时,才会在出错时调用 sys.exit。

,

这是可同时用作 cli 和库的包结构的最小示例。

这是目录结构:

egpkg/
├── setup.py
└── egpkg/
   ├── __init__.py
   ├── lib.py
   └── cli.py

这是 entry_points 中的 setup.py。它与您的相同:

    entry_points={
        "console_scripts": [
            "egpkg_cli=egpkg.cli:main",],},

__init__.py

from .lib import func

cli.py
您将在此处定义 CLI 并处理您在其他 Python 文件中定义的函数引发的任何问题。

import sys
import argparse

from egpkg import func


def main():
    p = argparse.ArgumentParser()
    p.add_argument("a",type=int)
    args = vars(p.parse_args())

    try:
        result = func(**args)
    except Exception as e:
        sys.exit(str(e))

    print(f"Got result: {result}",file=sys.stdout)

lib.py
这是定义库的核心的地方,您应该以您期望用户使用它的方式使用该库。当您获得不起作用的值/输入时,您可以raise 它。

def func(a):
    if a == 0:
        raise ValueError("Supplied value can't be 0")
    return 10 / a

然后在 python 控制台或脚本中,您可以:

In [1]: from egpkg import func
In [2]: func(2)
Out[2]: 5.0
In [3]: func(0)
Traceback (most recent call last):
  File "<stdin>",line 1,in <module>
  File "/tmp/egpkg/egpkg/lib.py",line 3,in func
    raise ValueError("Supplied value can't be 0")
ValueError: Supplied value can't be 0

Supplied value can't be 0

来自 CLI:

(venv) ~ egpkg_cli 2
Got result: 5.0
(venv) ~ egpkg_cli 0
Supplied value can't be 0

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?