Search code examples
pythonc++python-extensions

Trouble with DateTime object methods in C++ extension


I'm working on a C++ extension for Python 3 and trying to pass a DateTime object to my function. the PyDateTime_Check function seems to work and returns true, but when I try to get the year from the DateTime object it returns 65535. I'm passing datetime.datetime.now() into the function from python.

my c++ code is:

#include "Python.h"
#include "datetime.h"

static PyObject* new_func(Pyobject* self, PyObject* args)
{
    PyDateTime_DateTime *pydate;
    if(!PyArg_ParseTuple(args, "O", &pydate)){
        return Py_BuildValue("s", "Error");
    }
    int year;
    year = PyDateTime_GET_YEAR(pydate);
    return PyLong_FromLong(year);
}

static PyMethodDef NbourMethods[] =
{
     {"new_func", new_func, METH_VARARGS, "Do date things."},
     {NULL, NULL, 0, NULL}
};

static struct PyModuleDef cFunc =
{
  PyModuleDef_HEAD_INIT,
  "cFunc", /* name of module */
  "",     /* module documentation */
  -1,     /* size of per-interpreter state of the module or -1 if global */
  NbourMethods
};

PyMODINIT_FUNC PyInit_cFunc(void)
{
  if (!PyDateTimeAPI) { PyDateTime_IMPORT;}
  return PyModule_Create(&cFunc);
}

Then my python code is:

import cFunc
start = datetime.datetime.now()
print(str(cFunc.new_func(start)))

Any thoughts? I'm able to successfully run PyDateTime_Check(pydate) and get a return value of true, but for some reason it won't return the year in the code above (returns 65535). Thanks in advance!


Solution

  • So after messing around some more there seemed to be an issue with both the compiler and the python library. Changing these yielded the desired results. I did have a weird hack below, but the extension module was using up about 4GB of RAM each time it was loaded -> indicating that there was something more wrong going on. Seems to be a potential issue in MinGW.

    My original solution:

    So just messing around and looking at the underlying datetime.h and doing some exploring - there seems to be 4 bytes in the PyObject for my date that are unused. Not sure why this is happening.

    An interim fix is to return the year by referencing bytes 4 and 5 (as opposed to 0 and 1 in the function):

    year = ((((PyDateTime_Date *)pydate)->data[4] << 8) | ((PyDateTime_Date *)pydate)->data[5]);
    

    This isn't great, but an alternative would be to define my own GET_YEAR function after datetime.h is imported:

    #define PyDateTime_GET_YEAR_NEW(o)     ((((PyDateTime_Date*)o)->data[4] << 8) | \
                     ((PyDateTime_Date*)o)->data[5])
    

    Then the return in the function could be:

    return PyLong_FromLong(PyDateTime_GET_YEAR_NEW(pydate));