0

I'm writing a python module in C via the Python C API. What I want to do is perhaps best illustrated by this short python snippet:

def convert(value, func):
    if isinstance(value, decimal.Decimal):
        return func(value)
    return value

where func would most likely be str or float, but could be a user-defined function that takes a single argument. I've been struggling to answer a few questions about this approach.

  1. How do you check for the type decimal.Decimal. I can see in the decimal code that they've defined some un-exported macros and typedefs that would assist this this, but as they are un-exported I cannot use them (as far as I know). I searched SO and found some posts that look promising but as it is a question/answer from 9 years ago, I'm not sure if its the best way to do this.

  2. I've experimented with passing str and float into the function I've defined in C, but I can't get them to execute correctly. I'm not sure how to use a PyObject as a function pointer and invoke it. The closest thing I found was PyObject_Call but float and str appear to not be callables. Do I need to do something else like take an argument that can be a string ('float', or 'str') and then call PyObject_Str or PyFloat_FromString on the decimal object (once I've identified it as a decimal), or is there some way to pass into the C API a generic function pointer that takes a single positional argument and invoke it with a argument/value of my choosing?

UPDATE: I've learned how to do #2. You can use

PyObject_CallObject(func, PyTuple_Pack(1, value))

to call a function pointer passed into the C function.

Bryant
  • 3,011
  • 1
  • 18
  • 26
  • I'm not exactly familiar with the c api, but by importing the decimal module you don't get either `PyDec_Check` or `PyDec_Type`? if is just PyDec_Check that you don't get because is in a `#define` but you get the PyDec_Type then you can make PyDec_Check again because `PyObject_TypeCheck` is part of the api – Copperfield Sep 15 '21 at 21:09
  • @Copperfield its not defined in a header file, so no, I don't think thats possible. – Bryant Sep 15 '21 at 21:16
  • the header file the .h, right? where is the one from decimal? – Copperfield Sep 15 '21 at 21:37
  • @Copperfield thats my point - there isn't one. – Bryant Sep 15 '21 at 22:44
  • so c doesn't have a way to import stuff if that stuff doesn't have a .h? that sound kind of weird to me, well regardless, maybe try asking in the python bug tracker or to Raymond Hettinger (raymondh) or Guido van Rossum (gvanrossum) on twitter ) – Copperfield Sep 16 '21 at 00:35
  • @Copperfield I certainly didn't say that. Like most languages you can hack around it and do some things to import things that aren't exported, but if they aren't designed to be exposed you risk them changing on you and breaking your code since they aren't part of a clearly defined interface. I'm going for the correct/preferred way here, not just something that works – Bryant Sep 16 '21 at 12:29

1 Answers1

1

I've eventually discovered how to do the two parts.

For 1.

PyObject_CallObject(func, PyTuple_Pack(1, value))

(note that PyTuple_Pack returns a new reference, so this will need to be decremented after use, I've omitted that for clarity).

for 2.

int _check_decimal(PyObject *value)
{
    PyObject *module = PyImport_ImportModule((char *) "decimal");
    PyObject* moduleDict = PyModule_GetDict(module);
    PyObject* class = PyDict_GetItemString(moduleDict, "Decimal");

    return PyObject_IsInstance(value, class);
}

(again note that some of these return references that need to be managed)

Bryant
  • 3,011
  • 1
  • 18
  • 26