Как разобрать требуемый взаимоисключающий аргумент в Python C-api
Как можно разобрать группу обязательных, но взаимоисключающих аргументов, используя Python C-api?
Например, дано определение функции
static PyObject* my_func(PyObject *self, PyObject *args, PyObject *kwargs) {
double a; // first argument, required
double b=0, c=0; // second argument, required but mutually exclusive, b is default keyword if no keyword is set
char d[] = "..."; // third argument, optional
// parse arguments
...
}
Моя идея заключалась в том, чтобы дважды проанализировать входные аргументы, т.е. заменить ...
выше с:
static const char *kwList1[] = {"a","b","c","d"};
static const char *kwList2[] = {"a","b","d"};
int ret;
if (!(ret = PyArg_ParseTupleAndKeywords(args,kwargs,"d|dds",(char **)kwList1,&a,&b,&c,&d))) {
ret = PyArg_ParseTupleAndKeywords(args,kwargs,"d|ds",(char **)kwList2,&a,&b,&d));
}
if (!ret) return NULL;
// verify that one of, but not both, variables b and c are non-zero
...
Тем не менее, второй звонок PyArg_ParseTupleAndKeywords()
возвращает 0 для правильного ввода, поэтому я предполагаю, что переменные args
а также kwargs
есть некоторые атрибуты, установленные при первом вызове PyArg_ParseTupleAndKeywords()
это приводит к сбою второго вызова (выходная ошибка Python: TypeError: требуется float).
Я знаю, что вышеизложенное можно решить с помощью argparse
модуль Python, но предпочел бы решение непосредственно с использованием C-API. Одна идея здесь была бы, если бы была возможность первой копии ввода args
а также kwargs
на два новых PyObject
переменные и использовать их во втором вызове PyArg_ParseTupleAndKeywords()
Однако я не могу найти какую-либо API-функцию для этого (думаю, мне также нужно знать, как освободить память, выделенную для этого).
1 ответ
Похоже, проблема заключалась в том, что первый звонок PyArg_ParseTupleAndKeywords()
установите индикатор ошибки, который вызвал сбой второго вызова функции. Таким образом, решение заключается в том, чтобы вставить вызов PyErr_Clear()
между звонками PyArg_ParseTupleAndKeywords()
, Таким образом, следующий код выполняет задачу
static PyObject* my_func(PyObject *self, PyObject *args, PyObject *kwargs) {
double a; // first argument, required
double b=0, c=0; // second argument, required but mutually exclusive, b is default keyword if no keyword is set
char d[] = "..."; // third argument, optional
// parse arguments
static const char *kwList1[] = {"a","b","c","d"};
static const char *kwList2[] = {"a","b","d"};
int ret;
if (!(ret = PyArg_ParseTupleAndKeywords(args,kwargs,"d|dds",(char **)kwList1,&a,&b,&c,&d))) {
PyErr_Clear();
ret = PyArg_ParseTupleAndKeywords(args,kwargs,"d|ds",(char **)kwList2,&a,&b,&d));
}
if (!ret) return NULL;
// verify that one of, but not both, variables b and c are non-zero
if (b==0 && c==0) {
PyErr_SetString(PyExc_TypeError,"Required mutually exclusive arguments 'b' or 'c' (pos 2) not found (or input with value 0)");
return NULL;
} else if (b!=0 && c!=0) {
PyErr_SetString(PyExc_TypeError,"Use of multiple mutually exclusive required arguments 'b' and 'c' (pos 2)");
return NULL;
}
...
}
Опять же, это не защищает от вызова функции с обоими аргументами b
а также c
учитывая, что один из них равен 0, а другой нет. Однако это небольшая проблема.