恢复原始argv

时间:2018-03-22 13:00:54

标签: python python-3.x command-line-arguments

当使用python显式调用脚本时,argv会被删除,以便argv[0]是正在运行的脚本的路径。如果以python foo/bar.pypython -m foo.bar调用,则会出现这种情况。

我需要一种方法来恢复原始argv(即python收到的那个)。遗憾的是,它并不像sys.executable前置sys.argv那么容易,因为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']

我知道有关此事的先前问题:

但是这些似乎对炮弹如何工作有误解,答案反映了这一点。我对撤消shell的工作不感兴趣(例如,评估的shell变量和函数都很好),我只想获得给argv的原始python

我发现的only solution是使用/proc/<PID>/cmdline

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

这确实有效,但它只支持Linux(没有OSX,Windows支持似乎需要安装wmi package)。幸运的是,对于我目前的用例,此限制很好。但是,拥有更清洁,跨平台的方法会很好。

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

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

修改从洞穴探险中,我发现了Py_GetArgcArgv,可以通过ctypes访问(找到它here,链接到several SO帖子提到这种方法):

blah.py

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

2 个答案:

答案 0 :(得分:1)

这似乎是XY问题而且你正在进入杂草以适应一些现有的复杂测试设置(我在comment中找到了问题背后的问题)。在编写理智的测试设置时,可以更好地花费更多精力。

  1. 使用better test runner,而不是unittest。
  2. 在进入Python运行时之前,在测试设置中创建任何初始状态,而不是在外部环境中创建。
  3. 使用插件进行随机化和播种,我个人使用this one但还有其他人。
  4. 例如,如果您决定使用pytest runner,则可以在[tool:pytest]文件的setup.cfg部分和/或fixture设置pytest 部分内配置所有测试设置({{ 3}})。覆盖默认测试配置可以使用环境变量和/或命令行参数来完成,这些方法都不会被shell破解或在Python解释器启动期间。

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

    sys.argv

    然后,您认为需要恢复原始'use strict'; const functions = require('firebase-functions'); const nodemailer = require('nodemailer'); const SEND_GRID_API_KEY = functions.config().sendgrid.key const sgMail = require('@sendgrid/mail'); sgMail.setApiKey(SEND_GRID_API_KEY); // Your company name to include in the emails // TODO: Change this to your app or company name to customize the email sent. const APP_NAME = 'Asia Rubber'; // [START sendWelcomeEmail] /** * Sends a welcome email to new user. */ // [START onCreateTrigger] exports.sendWelcomeEmail = functions.auth.user().onCreate((event) => { // [END onCreateTrigger] // [START eventAttributes] const user = event.data; // The Firebase user. const email = user.email; // The email of the user. const displayName = user.displayName; // The display name of the user. // [END eventAttributes] return sendWelcomeEmail(email, displayName); }); // [END sendWelcomeEmail] // [END sendByeEmail] // Sends a welcome email to the given user. function sendWelcomeEmail(email, displayName) { const msg = { from: `${APP_NAME} <Welcome@rubber.asia>`, to: email, subject: 'Welcome to ${APP_NAME}!', //custom templates templateId: 'e3978a51-5343-4b3a-9128-8c4a493f265e' }; return sgMail.send(msg) .then( () => console.log('email sent')) .catch(err => console.log(err)) } 的问题将会消失。

答案 1 :(得分:0)

您说明的问题是:

  1. 用户使用环境变量和参数调用我的应用程序。
  2. 我想显示一个“像这样运行”的诊断,它将精确地重现当前运行的结果。
  3. 至少有两种解决方案:

    1. 放弃“复制”方面,因为原始的bash调用命令丢失到便携式python应用程序,而是去“相同的效果”。
    2. 使用包装器捕获原始调用命令,如Jean-FrançoisFabre所建议。
    3. 用(1)你愿意接受['-m','foo']成为['foo.py'],或者甚至把它变成['/some/dir/foo.py']案例PYTHONPATH可能会造成麻烦。将['a','b c']显示为"a" "b c",或更简洁地显示为a "b c",这很简单。如果像SEED这样的环境变量是命令行界面的重要部分,那么你需要迭代envp并输出它们。为了真正的可重复性,您可以选择将输入参数转换为规范形式,与观察到的输入参数进行比较,如果它们不相同则使用规范形式执行,因此无法使用“奇数”语法执行大量代码

      使用(2)你可以将应用程序隐藏在一个不方便命名的文件中,广泛地宣传包装程序,并享受在args之前看到args的好处。