如何获得 python 解释器完整的 argv 命令行选项?

How to get python interpreter full argv command line options?

我们从文档中得知:

-c If this option is given, the first element of sys.argv will be "-c" and the current directory will be added to the start of sys.path (allowing modules in that directory to be imported as top level modules).

如何获得完整的解释器命令行选项?我需要它来解决这个问题:

https://github.com/mitsuhiko/werkzeug/blob/f50bdc04cf1c8d71d12d13a0c8ef2878477f4d24/werkzeug/_reloader.py#L141

如果我启动 werkzeug 开发服务器,那么它将在 fork 上丢失 -c cmd 选项。我想给 werkzeug 打补丁,但找不到如何获得真正的选项。

如果您想知道我为什么需要这个 - 我想在 manage.py 之前预先执行一些要解析 sys.argv 的代码。而且我认为 werkzeug 方法不正确,因为它在极端情况下不起作用。

If I start werkzeug development server, then it will lost -c cmd option on fork.

首先,进程不是简单的分叉。一个新的 Python 解释器被调用。

it will lost -c cmd 是什么意思? cmd 字符串在 argv 中消失了吗?即:

$ python -c "import sys; print(sys.argv)"
['-c']

确实,cmd 字符串无法从 sys.argv 中访问。 This是相关文档:

If the command was executed using the -c command line option to the interpreter, argv[0] is set to the string '-c'

文档不评论实际的命令字符串。虽然该命令字符串显然是 "sent" 作为 Python 解释器可执行文件的参数,但 CPython 实现似乎并未在 sys.argv 中公开此信息。我想如果不更改 sysmodule.c 的源代码,就没有办法重建这些信息。所以,如果您认为自己依赖于提取 cmd —— 您不应该这样做!你需要找到另一种方式来注入这些信息。

编辑:

实际命令字符串在函数Modules/main.c中消耗 Py_Main():

wcscpy(command, _PyOS_optarg);

command 是稍后在 main.c 中执行的内容。

命令行参数通过 PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind); 处理,后者又在 sysmodule.c 中调用 makeargvobject()。后一个函数在类似 for (i = 0; i < argc; i++) {} 的循环中将二进制参数数据转换为 Python unicode 对象(至少在 Python 3 中是这样)。因此,argc 必须(故意)关闭 -1 以忽略所述循环中的命令。

也就是说,删除命令参数的神奇之处在于设置 _PyOS_optind,以便随后调用 PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind); 表明参数计数比实际值小(1)。

我没有真正跟进,但我猜这些行中的递减是负责任的:

if (command != NULL) {
    /* Backup _PyOS_optind and force sys.argv[0] = '-c' */
    _PyOS_optind--;
    argv[_PyOS_optind] = L"-c";
}

编辑2:

通过对当前 Python 3 提示的以下补丁验证了 _PyOS_optind 的关键作用:

diff --git a/Modules/main.c b/Modules/main.c
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -679,9 +679,11 @@
     }

     if (command != NULL) {
         /* Backup _PyOS_optind and force sys.argv[0] = '-c' */
         _PyOS_optind--;
-        argv[_PyOS_optind] = L"-c";
+        _PyOS_optind = 0;
+        //argv[_PyOS_optind] = L"-c";
     }

     if (module != NULL) {

测试:

 $ ./python -c "import sys; print(sys.argv)"
['./python', '-c', 'import sys; print(sys.argv)']