恢复原始参数



当使用python显式调用脚本时,argv被破坏,因此argv[0]是正在运行的脚本的路径。如果作为python foo/bar.py甚至python -m foo.bar调用,则就是这种情况。

我需要一种方法来恢复原始argv(即python收到的)。不幸的是,这并不像在sys.argv前面加上sys.executable那么容易,因为python foo/bar.pypython -m foo.bar不同(隐式PYTHONPATH不同,这可能至关重要,具体取决于您的模块结构)。

更具体地说,在python foo/bar.py some other argspython -m foo.bar some other args的情况下,我希望分别恢复['python', 'foo/bar.py', 'some', 'other', 'args']['python', '-m', 'foo.bar', 'some', 'other', 'args']

我知道之前关于这个问题的问题:

  • 如何在 python 中获取 ORIGINAL 命令行? 带有空格、制表符等
  • 键入的完整命令行

但这些似乎对贝壳的工作原理存在误解,答案反映了这一点。我对撤消 shell 的工作不感兴趣(例如,评估的 shell vars 和函数很好),我只想了解提供给python的原始argv

我找到的唯一解决方案是使用/proc/<PID>/cmdline

import os
with open("/proc/{}/cmdline".format(os.getpid()), 'rb') as f:
original_argv = f.read().split('')[:-1]

这确实有效,但它仅适用于Linux(没有OSX,Windows支持似乎需要安装wmi软件包)。幸运的是,对于我当前的用例,此限制很好。但是,最好有一个更干净的跨平台方法。

事实上,这种/proc/<PID>/cmdline方法有效,这给了我希望 python 在运行脚本之前不会执行(至少不是 syscall exec,但可能是内置的exec)。我记得在某处读到所有这些参数处理(例如。-m) 是用纯 python 完成的,而不是 C(这得到了证实,python -m this.does.not.exist将产生一个看起来像来自运行时的异常)。所以,我冒昧地猜测,在纯python的某个地方,原始argv是可用的(也许这需要通过运行时初始化进行一些探索?

tl;dr是否有一种跨平台(最好是内置的)方法来获取传递给python的原始argv(在它删除python可执行文件并将-m blah转换为blah.py之前)?

编辑从洞穴探险中,我发现了Py_GetArgcArgv,可以通过ctypes访问(在这里找到它,链接到几个提到这种方法的SO帖子):

import ctypes
_argv = ctypes.POINTER(ctypes.c_wchar_p)()
_argc = ctypes.c_int()
ctypes.pythonapi.Py_GetArgcArgv(ctypes.byref(_argc),
ctypes.byref(_argv))
argv = _argv[:_argc.value]
print(argv)

现在这是操作系统可移植的,但不是 python 实现可移植的(仅适用于 cpython,如果你不需要它ctypes很糟糕)。另外,奇怪的是,我在 Ubunutu 16.04 上没有得到正确的输出(python -m foo.bar给了我['python', '-m', '-m']),但我可能只是犯了一个愚蠢的错误(我在 OSX 上得到了相同的行为)。拥有一个完全便携式的解决方案(不会深入研究ctypes)会很棒。

Python3.10 添加了sys.orig_argv,文档将其描述为最初传递给 Python 可执行文件的参数。如果这不是您要查找的,则在这种或类似情况下可能会有所帮助。

考虑了很多可能性,包括更改sys.argv,但我认为这是明智地选择为最有效和无破坏性的选择。

这似乎是XY问题,并且您正在进入杂草以适应一些现有的复杂测试设置(我已经在您的评论中找到了问题背后的问题)。 进一步的努力最好花在编写一个健全的测试设置上。

  1. 使用更好的测试运行程序,而不是单元测试。
  2. 在进入 Python 运行时之前,在测试设置中创建任何初始状态,而不是在外部环境中创建
  3. 使用
  4. 插件进行随机化和种子的东西,我个人使用这个,但还有其他插件。

例如,如果您决定使用 pytest 运行器,则可以在pyproject.toml文件的[tool.pytest.ini_options]部分中和/或使用conftest.py中定义的夹具配置所有测试设置。 覆盖默认测试配置可以使用环境变量和/或命令行参数来完成,这些方法都不会被 shell 或 Python 解释器启动期间弄乱。

执行测试套件的方式可以而且应该像执行单个命令一样简单:

pytest

然后,您认为需要恢复原始sys.argv的问题将消失。

你陈述的问题是:

  1. 用户使用环境变量和参数调用我的应用。
  2. 我想显示一个"像这样运行"诊断,它将完全重现当前运行的结果。

至少有两种解决方案:

  1. 放弃"复制"方面,因为原始的bash调用命令丢失到可移植的python应用程序中,而是追求"相同的效果"。
  2. 使用包装器捕获原始调用命令,如让-弗朗索瓦·法布尔所建议的那样。

对于(1),你愿意接受['-m','foo']变成['foo.py'],甚至把它变成['/some/dir/foo.py'],以防PYTHONPATH可能造成麻烦。将 ['a', 'b c'] 显示为"a" "b c",或更简洁地显示为a "b c",很简单。 如果像 SEED 这样的环境变量是命令行界面的重要组成部分,那么你也需要迭代 envp 并输出它们。对于真正的可重现性,您可以选择将输入参数转换为规范形式,与观察到的输入参数进行比较,如果它们不相同,则使用规范形式执行,因此无法使用"奇数"语法执行大部分代码。

使用 (2) 您可以将应用程序埋在一些不方便命名的文件中,广泛宣传包装程序,并享受在被咀嚼之前看到 args 的好处。

相关内容

最新更新