1

我正在向 Exim 添加一个嵌入式 python 解释器。我已经复制了嵌入式 perl 接口,并希望 python 与长期以来编码的嵌入式 perl 解释器一样工作。目标是允许系统管理员使用强大的脚本语言(即python)执行复杂的功能,而不是尝试使用exim 的标准ACL 命令,因为使用exim ACL 语言执行相对简单的事情会变得相当复杂。

在撰写本文时,我当前的代码位于http://git.exim.org/users/tlyons/exim.git/blob/9b2c5e1427d3861a2154bba04ac9b1f2420908f7:/src/src/python.c。它可以正常工作,因为它可以导入系统管理员的自定义 python 代码,调用其中的函数,并处理返回值(仅限简单返回类型:int、float 或 string)。但是,它还没有处理传递给 python 函数的值,这是我的问题开始的地方。

Python 似乎要求使用 c api 将我传递给嵌入式 python 函数的任何参数显式转换为 int、long、double、float 或 string 之一。问题是系统管理员可以在嵌入的 python 代码和 exim 的 c 方面放入任何东西,我不知道这些变量类型是什么。我知道 python 是动态类型的,所以我希望在将值传递给嵌入式代码时保持这种合规性。但在我的测试中它不是这样工作的。

使用以下基本的超简单 python 代码:

def dumb_add(a,b):
        return a+b

...而我的 exim ACL 语言的调用代码是:

${python {dumb_add}{800}{100}}

在下面的 c 代码中,为简洁起见,省略了引用计数。 count是我传递的参数数量:

pArgs = PyTuple_New(count);
for (i=0; i<count; ++i)
{
  pValue = PyString_FromString((const char *)arg[i]);
  PyTuple_SetItem(pArgs, i, pValue);
}
pReturn = PyObject_CallObject(pFunc, pArgs);

是的,**arg 是一个指向字符串数组的指针(在这个简单的例子中是两个字符串)。问题是这两个值在 python 代码中被视为字符串,因此该 c 代码执行嵌入式 python 的结果是:

${python {dumb_add}{800}{100}}

800100

如果我将python更改为:

def dumb_add(a,b):
        return int(a)+int(b)

然后该 c 代码执行 python 代码的结果与预期的一样:

${python {dumb_add}{800}{100}}

900

我的目标是我不想强迫 python 用户手动将他们传递给嵌入式 python 函数的所有数字参数强制转换。如果有一个 PyDynamicType_FromString(),而不是 PyString_FromString(),我会欣喜若狂。Exim 的嵌入式 perl 解析 args 并自动进行转换,我希望嵌入式 python 也能做到这一点。任何人都可以建议 python 是否可以执行此 arg 解析以提供我期望的动态类型?

或者,如果我想保持这种动态类型,我唯一的选择是解析每个 arg 并猜测将其转换为的类型吗?我真的真的真的很希望避免这种方法。如果涉及到这一点,我可能只是记录“所有传递的参数都是字符串,所以如果你真的想传递数字,你必须用 int()、float()、double() 或 long() 转换所有参数” . 然而,后面总是有一个逗号,我觉得这种方法会使我的实现中强大的 python 编码器变得糟糕。我也想避免这种情况。

除了“将您的应用程序变成 python 模块”之外,任何和所有建议都将受到赞赏。

4

1 回答 1

0

我最终解决这个问题的方法是找出函数预期的参数数量,如果传递给函数的参数数量不匹配,则退出并返回错误。与其尝试合成缺失的 args 或简单地省略额外的 args,对于我的用例,我觉得最好强制匹配 arg 计数。

args 作为unsigned char ** arg:

  int count = 0;
  /* Identify and call appropriate function */
  pFunc = PyObject_GetAttrString(pModule, (const char *) name);
  if (pFunc && PyCallable_Check(pFunc))
  {
    PyCodeObject *pFuncCode = (PyCodeObject *)PyFunction_GET_CODE(pFunc);
    /* Should not fail if pFunc succeeded, but check to be thorough */
    if (!pFuncCode)
    {
      *errstrp = string_sprintf("Can't check function arg count for %s",
                                name);
      return NULL;
    }
    while(arg[count])
      count++;
    /* Sanity checking: Calling a python object requires to state number of
       vars being passed, bail if it doesn't match function declaration. */
    if (count != pFuncCode->co_argcount)
    {
      *errstrp = string_sprintf("Expected %d args to %s, was passed %d",
                                pFuncCode->co_argcount, name, count);
      return NULL;
    }

string_sprintf是 Exim 源代码中的一个函数,它还处理内存分配,让我的生活变得轻松。

于 2013-09-11T13:49:35.720 回答