0

I am trying to use the Python 3.5 C API to execute some code that includes constructing a class. Specifically this:

class MyClass:
    def test(self):
        print('test')

MyClass().test()

The problem I have is that it errors like this:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
NameError: __build_class__ not found

So somehow I need my module to include __build_class__, but I am not sure how (I guess that I would also miss other things you get by default when using Python too) - is there a way to include all this built-in stuff in my module?

Here is my code so far:

#include <Python.h>

int main(void)
{
    int ret = 0;
    PyObject *pValue, *pModule, *pGlobal, *pLocal;

    Py_Initialize();

    pGlobal = PyDict_New();
    pModule = PyModule_New("mymod");
    pLocal = PyModule_GetDict(pModule);

    pValue = PyRun_String(
        "class MyClass:\n\tdef test(self):\n\t\tprint('test')\n\nMyClass().test()",
        Py_file_input,
        pGlobal,
        pLocal);

    if (pValue == NULL) {
        if (PyErr_Occurred()) {
            PyErr_Print();
        }
        ret = 1;
    } else {
        Py_DECREF(pValue);
    }

    Py_Finalize();

    return ret;
}

so pValue is NULL and it is calling PyErr_Print.

5
  • I think you need to pass it the interpreter globals instead of an empty dictionary. But I can't see how to do it. Commented Oct 12, 2016 at 9:08
  • Actually, probably see stackoverflow.com/a/25907027/4657412 Commented Oct 12, 2016 at 9:09
  • Thanks, sadly PyEval_GetGlobals(); returns null (I get SystemError: PyEval_EvalCodeEx: NULL globals from the call). Do I need to import the main module or something like that perhaps? Commented Oct 12, 2016 at 9:13
  • Ahh, thanks to your tip off I started looking for GetGlobals returns null and found this: gossamer-threads.com/lists/python/python/8946#8946 - seems I need to do pGlobal = PyModule_GetDict(PyImport_AddModule("main")); (instead of pGlobal = PyDict_New()) and then it works. (there's underscores in that main, not bold) THANK YOU! Commented Oct 12, 2016 at 9:20
  • Glad to have pointed you on the right track. (I'm not sure I'd have got to the useful answer myself). By the way, you can put code in comments with backticks (`) Commented Oct 12, 2016 at 9:53

1 Answer 1

1

Their are (at least) two ways to solve this it seems...

Way 1

Instead of:

pGlobal = PyDict_New();

You can import the __main__ module and get it's globals dictionary like this:

pGlobal = PyModule_GetDict(PyImport_AddModule("__main__"));

This way is described like so:

BUT PyEval_GetGlobals will return null it it is not called from within Python. This will never be the case when extending Python, but when Python is embedded, it may happen. This is because PyRun_* define the global scope, so if you're not somehow inside a PyRun_ thing (e.g. module called from python called from embedder), there are no globals.

In an embedded-python situation, if you decide that all of your PyRun_* calls are going to use __main__ as the global namespace, PyModule_GetDict(PyImport_AddModule("__main__")) will do it.

Which I got from the question embedding I found over on this Python list.

Way 2

Or as an alternative, which I personally prefer to importing the main module (and found here), you can do this to populate the new dictionary you created with the built-in stuff which includes __build_class__:

pGlobal = PyDict_New();
PyDict_SetItemString(pGlobal, "__builtins__", PyEval_GetBuiltins());
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.