当使用python
显式调用脚本时,argv
会被删除,以便argv[0]
是正在运行的脚本的路径。如果以python foo/bar.py
或python -m foo.bar
调用,则会出现这种情况。
我需要一种方法来恢复原始argv
(即python
收到的那个)。遗憾的是,它并不像sys.executable
前置sys.argv
那么容易,因为python foo/bar.py
与python -m foo.bar
不同(隐式PYTHONPATH
不同,这取决于您的要求模块结构)。
更具体地说,在python foo/bar.py some other args
和python -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']
)会很棒。
答案 0 :(得分:1)
这似乎是XY问题而且你正在进入杂草以适应一些现有的复杂测试设置(我在comment中找到了问题背后的问题)。在编写理智的测试设置时,可以更好地花费更多精力。
例如,如果您决定使用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)你愿意接受['-m','foo']成为['foo.py'],或者甚至把它变成['/some/dir/foo.py']案例PYTHONPATH可能会造成麻烦。将['a','b c']显示为"a" "b c"
,或更简洁地显示为a "b c"
,这很简单。如果像SEED这样的环境变量是命令行界面的重要部分,那么你需要迭代envp并输出它们。为了真正的可重复性,您可以选择将输入参数转换为规范形式,与观察到的输入参数进行比较,如果它们不相同则使用规范形式执行,因此无法使用“奇数”语法执行大量代码
使用(2)你可以将应用程序隐藏在一个不方便命名的文件中,广泛地宣传包装程序,并享受在args之前看到args的好处。