osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

C API version of str(exception) is not the same as pure python version


When I use the C API to get the str() of an execption I see this text:

	<class 'TypeError'>

But python reports the following for the same exception:

	TypeError: unsupported operand type(s) for +: 'int' and 'str'

What do I need to do in the C API to get the the same text for the exception?

All the code I'm using is below. I have been debugging this on Fedora 29 with 
python 3.7.2.

To repoduce the problem put the three files in a folder and run:

$ py3 setup.py build
$ PYTHONPATH=build/lib.linux-x86_64-3.7  python3 simple_test.py

Which will output:

Error: <class 'TypeError'>
Traceback (most recent call last):
  File "simple_test.py", line 7, in <module>
    simple.test_error()
  File "simple_test.py", line 5, in simple_eval
    return eval( arg )
  File "<string>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

---- setup.py ---
import os
os.environ['CFLAGS'] = "-O0 -g"
from distutils.core import setup, Extension
setup(name = 'simple', version = '1.0',
   ext_modules = [Extension('simple', ['simple.c'])])

--- simple.c ---
#include <Python.h>
#include <stdio.h>

// Yes I know the ref counts are not decremented
static PyObject *test_error(PyObject *self, PyObject *args)
{
    PyObject *m = PyImport_AddModule( "__main__" );
    PyObject *m_dict = PyModule_GetDict( m );
    PyObject *func_args = PyTuple_New( 1 );
    PyObject *str = PyUnicode_FromString( "1 + 'q'" );
    PyTuple_SetItem( func_args, 0, str);
    PyObject *func = PyMapping_GetItemString( m_dict, "simple_eval" );
    assert( func != 0 );
    PyObject *result = PyObject_CallObject( func, func_args );

    PyObject *err = PyErr_Occurred();
    if( err != 0 )
    {
        PyObject *u_why = PyObject_Str( err );
        PyObject *b_why = PyUnicode_AsEncodedString( u_why, "utf-8", "strict" 
);
        int size = PyBytes_Size( b_why );
        char *why = PyBytes_AsString( b_why );
        printf("Error: %*s\n", size, why );
        return 0;
    }

    return result;
}

static PyMethodDef myMethods[] = {
    { "test_error", test_error, METH_NOARGS, "test eval with error" },
    { NULL, NULL, 0, NULL }
};

static struct PyModuleDef simple = {
    PyModuleDef_HEAD_INIT,
    "simple",
    "Simple Module",
    -1,
    myMethods
};

// Initializes our module using our above struct
PyMODINIT_FUNC PyInit_simple(void)
{
    return PyModule_Create(&simple);
}

--- simple_test.py ---
import sys
import simple

def simple_eval( arg ):
    return eval( arg )

simple.test_error()

--- end --

Barry