[Pyrex] problem with pyrex and cx_freeze (like py2exe)

Robby Dermody robbyd at u20.org
Sat Sep 10 00:50:13 CEST 2005


Hey guys,

I have a very low level problem here that I'm a bit stumped about.

Basically, I have a very large pyrex library (under Linux). It's 
actually a bunch of pyrex files compiled together. (For instance, 
mainmod.pyx, sub1.pyx, sub2.pyx, etc. where mainmod.pyx calls 
initsub1(), initsub2() so that both C and python functionality work in 
sub1 and sub2.) For more clarification, I compile these files together 
into a single module though this kind of line in my setup.py file:

    Extension("mainmod",
        ["mainmod.pyx", "sub1.pyx", "sub2.pyx", ... ] )


Although I have been told that this kind of usage of Pyrex is not 
standard and not supported, the whole library works wonderfully. It's 
fast, and I can make C calls from file to file directly (and Python 
calls in any file work fine...it has been tested quite extensively).

FYI, I've written on this weird usage of pyrex before, see:
http://lists.copyleft.no/pipermail/pyrex/2005-February/001126.html

Alright, so this pyrex module works fine when I execute the python 
script that imports it directly with /usr/bin/python. However, when the 
library is frozen using cx_freeze (which is very much like py2exe, but 
works on Linux as well), I get a certain problem.

As a note of how cx_freeze runs python, it packages the python source 
tree in the executable, then uses zipimporter to get at it. 3rd party 
shared object libraries are simply dumped in the same directory as this 
executable, and sys.path is set up to contain the path of this 
executable, along with the executable itself.

So, what happens is that my python program gets converted into an 
executable. There is no change to the pyrex shared object library (it 
just is placed in the same directory of the frozen executable as I said 
earlier). When I run the frozen executable, it imports my pyrex library 
normally, and by doing low level traces I can see that the init() 
function for mainmod.pyx (initmainmod()) is called and completes fine. 
However, what goes wrong is that the init functions for each of the sub 
modules (sub1, sub2) fail at a certain point. These init functions are 
called from initmainmod() (but I have tried calling them from other 
places in mainmod.c too with the same results).

Here's the start of the pyrex-generated init function for one of these 
sub modules (happens to be called ar_voice). My comment for where in the 
code it fails is inline.

DL_EXPORT(void) initar_voice(void); /*proto*/
DL_EXPORT(void) initar_voice(void) {
  PyObject *__pyx_1 = 0;
  PyObject *__pyx_2 = 0;

  __pyx_m = Py_InitModule4("ar_voice", __pyx_methods, __pyx_mdoc, 0, 
PYTHON_API_VERSION);
  if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto 
__pyx_L1;};
  __pyx_b = PyImport_AddModule("__builtin__");
  if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto 
__pyx_L1;};
  if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) 
{__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};

  if (__Pyx_InternStrings(__pyx_intern_tab) < 0) {__pyx_filename = 
__pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
  if (__Pyx_InitStrings(__pyx_string_tab) < 0) {__pyx_filename = 
__pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
  __pyx_v_8ar_voice__pendingOperations = Py_None; 
Py_INCREF(__pyx_v_8ar_voice__pendingOperations);
  __pyx_type_8ar_voice_pendingOp.tp_free = _PyObject_GC_Del;
  if (PyType_Ready(&__pyx_type_8ar_voice_pendingOp) < 0) {__pyx_filename 
= __pyx_f[0]; __pyx_lineno = 37; goto __pyx_L1;}

^^^ fails at this PyType_Ready call, and the init function terminates 
early. Why this call is made is because I have a global level "cdef 
object pendingOp" declaration in the ar_voice module.

  if (PyObject_SetAttrString(__pyx_m, "pendingOp", (PyObject 
*)&__pyx_type_8ar_voice_pendingOp) < 0) {__pyx_filename = __pyx_f[0]; 
__pyx_lineno = 37; goto __pyx_L1;}
  __pyx_ptype_8ar_voice_pendingOp = &__pyx_type_8ar_voice_pendingOp;

[....]

As a result of this failure, whenever any function in this sub module 
tries to access any imported python library, it fails (obviously, 
because the Py_Import calls a made later in the init function, and it 
quits before that). What I'm wondering is why it might be failing at 
that PyType_Ready() call, and what I can do to fix that.

So I guess that things work fine if I'm executing through regular 
python, but through however cx_freeze executes, I get this type of 
failure. Maybe this is what Greg meant by "unsupported" and "wacky 
python behaviour", haha. :/

Here is some relevant C code for how cx freeze initializes python and 
starts my app. How this is different from how /usr/bin/python starts my 
app will probably contain the secret to why I am getting this problem 
(although with my limited knowledge I can't see it):

cx_freeze main:

int main(int argc, char **argv)
{
    const char *fileName;

    // initialize Python
    Py_NoSiteFlag = 1;
    Py_FrozenFlag = 1;
    Py_IgnoreEnvironmentFlag = 1;
    Py_SetPythonHome("");
    Py_SetProgramName(argv[0]);
    fileName = Py_GetProgramFullPath();
    Py_Initialize();
    PySys_SetArgv(argc, argv);

    // do the work
    if (ExecuteScript(fileName) < 0)
        return 1;

    Py_Finalize();
    return 0;
}

ExecuteScript looks like:

static int ExecuteScript(
    const char *fileName)               // name of file containing 
Python code
{
    PyObject *module, *importer, *code, *dict, *pathList, *temp;
    int result;
    // add the file to sys.path
    pathList = PySys_GetObject("path");
    if (!pathList)
        return FatalError("cannot acquire sys.path");
    temp = PyString_FromString(fileName);
    if (!temp)
        return FatalError("cannot create Python string");
    result = PyList_Insert(pathList, 0, temp);
    Py_DECREF(temp);
    if (result < 0)
        return FatalError("cannot insert file name in sys.path");

    // load and execute initscript
    module = PyImport_ImportModule("zipimport");
    if (!module)
        return FatalError("cannot import zipimport module");
    importer = PyObject_CallMethod(module, "zipimporter", "s", fileName);
    Py_DECREF(module);
    if (!importer)
        return FatalError("cannot get zipimporter instance");
    code = PyObject_CallMethod(importer, "get_code", "s", 
"cx_Freeze__init__");
    Py_DECREF(importer);
   if (!code)
        return FatalError("unable to locate initialization module");
    dict = PyDict_New();
    if (!dict)
        return FatalError("unable to create temporary dictionary");
    if (PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) 
< 0)
        return FatalError("unable to set builtins for initialization 
module");
    temp = PyEval_EvalCode( (PyCodeObject*) code, dict, dict);
    Py_DECREF(code);
    Py_DECREF(dict);
    if (!temp)
        return FatalScriptError();
    Py_DECREF(temp);

    return 0;
}



ANY ideas would be appreaciated!


Robby








More information about the Pyrex mailing list