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

Другие вопросы по тегам