【问题标题】:Dynamic arg types for a python function when embedding嵌入时python函数的动态arg类型
【发布时间】:2013-05-21 18:45:35
【问题描述】:

我正在向 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 解析以提供我期望的动态类型?

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

除了“将你的应用程序变成一个 python 模块”之外,任何和所有建议都值得赞赏。

【问题讨论】:

  • 你试过用PyRun_String代替PyString_FromString吗?
  • PyRun_String 如果只调用一个函数就足够了。但期望系统管理员可以定义 1、5、20 个函数,无论他需要多少,并随时从 Exim ACL 调用它们以用于各种目的。
  • 我的评论只是为了解决动态类型。因此,在您的 dumb_add 示例中,800 和 100 将使用 pValue = PyRun_String((const char *)arg[i],...); 之类的东西转换为 PyObjects。在这种情况下,arg[i] 将是“800”或“600”。因此,您正在“运行”字符串“800”,它应该返回一个整数类型的 PyObject。这有意义吗?

标签: python-c-api exim python-embedding


【解决方案1】:

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

参数作为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 源代码中的一个函数,它还处理内存分配,让我的生活变得轻松。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 2022-01-16
    • 1970-01-01
    • 2016-01-09
    • 2019-10-27
    • 2019-12-19
    相关资源
    最近更新 更多