5

I have this code in a C application that's embedding Python (2.7.1):

{
PyObject *user_dict;
PyObject *user_func;
PyObject *result;
PyObject *header_tuple;
PyObject *original_recipients;
PyObject *working_recipients;

if (!Py_IsInitialized())
    {
    Py_Initialize();
    }

if (!expy_exim_dict)
    {
    PyObject *module = Py_InitModule(expy_exim_module, expy_exim_methods); /* Borrowed reference */
    Py_INCREF(module);                                 /* convert to New reference */
    expy_exim_dict = PyModule_GetDict(module);         /* Borrowed reference */
    Py_INCREF(expy_exim_dict);                         /* convert to New reference */
    }

if (!expy_user_module)
    {
    if (expy_path_add)
        {
        PyObject *sys_module;
        PyObject *sys_dict;
        PyObject *sys_path;
        PyObject *add_value;

        sys_module = PyImport_ImportModule("sys");  /* New Reference */
        if (!sys_module)
            {
            PyErr_Clear();
            *return_text = "Internal error, can't import Python sys module";
            log_write(0, LOG_REJECT, "Couldn't import Python 'sys' module");
            return PYTHON_FAILURE_RETURN;
            }

        sys_dict = PyModule_GetDict(sys_module);               /* Borrowed Reference, never fails */
        sys_path = PyMapping_GetItemString(sys_dict, "path");  /* New reference */

        if (!sys_path || (!PyList_Check(sys_path)))
            {
            PyErr_Clear();  /* in case sys_path was NULL, harmless otherwise */
            *return_text = "Internal error, sys.path doesn't exist or isn't a list";
            log_write(0, LOG_REJECT, "expy: Python sys.path doesn't exist or isn't a list");
            return PYTHON_FAILURE_RETURN;
            }

        add_value = PyString_FromString(expy_path_add);  /* New reference */
        if (!add_value)
            {
            PyErr_Clear();
            log_write(0, LOG_PANIC, "expy: Failed to create Python string from [%s]", expy_path_add);
            return PYTHON_FAILURE_RETURN;
            }

        if (PyList_Append(sys_path, add_value))
            {
            PyErr_Clear();
            log_write(0, LOG_PANIC, "expy: Failed to append [%s] to Python sys.path", expy_path_add);
            }

        Py_DECREF(add_value);
        Py_DECREF(sys_path);
        Py_DECREF(sys_module);
        }

    expy_user_module = PyImport_ImportModule(expy_scan_module);  /* New Reference */

    if (!expy_user_module)
        {
        PyErr_Clear();
        /* Handle error */
        }
    }

When PyImport_ImportModule fails, it returns NULL. How can I find out why it failed to import? (e.g. when importing the module works find outside of the embedding).

(The code is part of py-exim-localscan, and I'm wanting to add more information about failures in the rare cases when they occur).

Amro
  • 123,847
  • 25
  • 243
  • 454
Tony Meyer
  • 10,079
  • 6
  • 41
  • 47
  • See also this question: http://stackoverflow.com/questions/1001216/python-c-api-how-to-get-string-representation-of-exception – Tony Meyer Jun 09 '11 at 11:26

1 Answers1

6

You do this by looking at the exception that was raised. Currently you wipe the exception (that's what PyErr_Clear() does.) Don't do that, and instead print the traceback or inspect the exception object. See http://docs.python.org/c-api/exceptions.html for information on how to do that from C code, but usually the best idea is to just let the exception propagate.

Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
  • Would letting the exception propagate not crash the C program? – Tony Meyer Jun 09 '11 at 10:54
  • That depends on the C program. If nothing handles the exception and you're not embedding Python, then Python will terminate (after printing the traceback.) If nothing handles the exception and you are embedding Python, the topmost function you called from your embedding program will simply return the error. But if something somewhere handles the exception (for example, in Python code that calls this function that tries to import the module) nothing needs to crash. – Thomas Wouters Jun 09 '11 at 10:58
  • The C code is executed directly, it's never called from Python. Can you explain what you mean by "embedding Python"? My (very limited) understanding is that executing Python from C is embedding (and writing a module in C and calling it from Python is extending). If Python terminates, does that leave my C program fine, just not able to use the Python API any more? – Tony Meyer Jun 09 '11 at 11:05
  • 1
    If the C code is the code that is embedding Python, you can't propagate the exception anywhere. You have to handle it right there (usually by printing the traceback.) The exception normally doesn't leave Python in an unusable state, unless the exception was caused by memory corruption or some abnormal state in the interpreter. – Thomas Wouters Jun 09 '11 at 11:46