как разобрать требуемый взаимоисключающий аргумент в Python C-API - PullRequest
0 голосов
/ 18 января 2019

Как можно разобрать группу обязательных, но взаимоисключающих аргументов с помощью 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(), что приводит к сбою второго вызова (ошибка выходного питона: TypeError: требуется плавающее число).

Я знаю, что вышесказанное можно решить с помощью модуля argparse python, но я бы предпочел решение, использующее C-api. Одна из идей здесь была бы, если бы можно было сначала скопировать входные данные args и kwargs в две новые переменные PyObject и использовать их во втором вызове PyArg_ParseTupleAndKeywords(), однако я не могу найти какой-либо Функция для этого (думаю, мне также нужно знать, как освободить память, выделенную для этого).

1 Ответ

0 голосов
/ 18 января 2019

Похоже, проблема заключалась в том, что первый вызов 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, а другой - нет. Однако это небольшая проблема.

...