Changeset 2230

Show
Ignore:
Timestamp:
07/01/07 08:45:42 (1 year ago)
Author:
abaumann
Message:

Don't assume that we are the only user of the Python interpreter in the
process. These fixes allow python plugins to work when other python
code uses the opensync bindings (eg. as an engine). Specifically:

  • Add the module directory to sys.path within the Python environment
    if it's not already there, rather than modifying PYTHONPATH and
    reinitialising the whole interpreter.
  • Change to use the PyGILState API instead of PyEval?_AcquireThread/
    ReleaseThread?. This simplifies the code, and means we don't have to
    worry about the state of all the threads in the system. The drawback
    is that we can't use a separate interpreter for each plugin, but
    it's up to the plugins to play nice anyway (and if they really need
    isolation then that should be supported by running in a separate
    process).
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/python-module/src/python_module.c

    r1788 r2230  
    3636 
    3737typedef struct MemberData { 
    38         PyThreadState *interp_thread; 
    3938        PyObject *osync_module; 
    4039        PyObject *module; 
     
    170169        osync_bool report_error = TRUE; 
    171170 
    172         PyEval_AcquireThread(data->interp_thread); 
     171        PyGILState_STATE pystate = PyGILState_Ensure(); 
    173172 
    174173        PyObject *pyinfo = pm_make_info(data->osync_module, info, &error); 
     
    205204                Py_DECREF(pycontext); 
    206205                Py_DECREF(ret); 
    207                 PyEval_ReleaseThread(data->interp_thread); 
     206                PyGILState_Release(pystate); 
    208207                osync_context_report_success(ctx); 
    209208                osync_trace(TRACE_EXIT, "%s", __func__); 
     
    260259 
    261260error: 
    262         PyEval_ReleaseThread(data->interp_thread); 
     261        PyGILState_Release(pystate); 
    263262        if (report_error) 
    264263                osync_context_report_osyncerror(ctx, error); 
     
    333332        osync_plugin_set_data(plugin, NULL); 
    334333 
    335         data->interp_thread = Py_NewInterpreter(); 
    336         if (!data->interp_thread) { 
    337                 PYERR_CLEAR(); 
    338                 osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't initialize python sub interpreter"); 
    339                 free(modulename); 
    340                 goto error; 
    341         } 
     334        PyGILState_STATE pystate = PyGILState_Ensure(); 
    342335 
    343336        if (!(data->module = PyImport_ImportModule(modulename))) { 
     
    376369        } 
    377370 
    378         PyEval_ReleaseThread(data->interp_thread); 
    379  
     371        PyGILState_Release(pystate); 
    380372        return data; 
    381373 
     
    383375        Py_XDECREF(data->module); 
    384376        Py_XDECREF(data->osync_module); 
    385         if (data->interp_thread) 
    386                 Py_EndInterpreter(data->interp_thread); 
     377        PyGILState_Release(pystate); 
    387378        free(data); 
    388379        return NULL; 
     
    393384        MemberData *data = data_in; 
    394385 
    395         PyEval_AcquireThread(data->interp_thread); 
     386        PyGILState_STATE pystate = PyGILState_Ensure(); 
    396387 
    397388        PyObject *pyinfo = pm_make_info(data->osync_module, info, error); 
     
    405396 
    406397        Py_DECREF(ret); 
    407         PyEval_ReleaseThread(data->interp_thread); 
     398        PyGILState_Release(pystate); 
    408399        return TRUE; 
    409400 
     
    411402        osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't call discover method"); 
    412403        PYERR_CLEAR(); 
    413         PyEval_ReleaseThread(data->interp_thread); 
     404        PyGILState_Release(pystate); 
    414405        return FALSE; 
    415406} 
     
    419410        osync_trace(TRACE_ENTRY, "%s(%p)", __func__, data); 
    420411        MemberData *mydata = data; 
    421         PyEval_AcquireThread(mydata->interp_thread); 
     412        PyGILState_STATE pystate = PyGILState_Ensure(); 
    422413 
    423414        /* free all sink objects */ 
     
    429420        Py_DECREF(mydata->module); 
    430421        Py_DECREF(mydata->osync_module); 
    431         Py_EndInterpreter(mydata->interp_thread); 
    432422        free(mydata); 
     423        PyGILState_Release(pystate); 
    433424        osync_trace(TRACE_EXIT, "%s", __func__); 
    434425} 
     
    528519} 
    529520 
     521/* set python search path to look in our module directory first */ 
     522static osync_bool set_search_path(OSyncError **error) 
     523{ 
     524        PyObject *sys_module = PyImport_ImportModule("sys"); 
     525        if (!sys_module) { 
     526                osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't import sys module"); 
     527                PYERR_CLEAR(); 
     528                return FALSE; 
     529        } 
     530         
     531        PyObject *path = PyObject_GetAttrString(sys_module, "path"); 
     532        if (!path) { 
     533                osync_error_set(error, OSYNC_ERROR_GENERIC, "sys module has no path attribute?"); 
     534                PYERR_CLEAR(); 
     535                Py_DECREF(sys_module); 
     536                return FALSE; 
     537        } 
     538         
     539        if (!PyList_Check(path)) { 
     540                osync_error_set(error, OSYNC_ERROR_GENERIC, "sys.path is not a list?"); 
     541                Py_DECREF(sys_module); 
     542                Py_DECREF(path); 
     543                return FALSE; 
     544        } 
     545         
     546        PyObject *plugindir = Py_BuildValue("s", OPENSYNC_PYTHONPLG_DIR); 
     547        if (!plugindir) { 
     548                osync_error_set(error, OSYNC_ERROR_GENERIC, "Error constructing plugindir string for sys.path"); 
     549                PYERR_CLEAR(); 
     550                Py_DECREF(sys_module); 
     551                Py_DECREF(path); 
     552                return FALSE; 
     553        } 
     554         
     555        int r = PySequence_Contains(path, plugindir); 
     556        if (r < 0) { 
     557                osync_error_set(error, OSYNC_ERROR_GENERIC, "Error checking for 'plugindir in sys.path'"); 
     558                PYERR_CLEAR(); 
     559                Py_DECREF(sys_module); 
     560                Py_DECREF(path); 
     561                Py_DECREF(plugindir); 
     562                return FALSE; 
     563        } 
     564         
     565        if (r == 0 && PyList_Insert(path, 0, plugindir) != 0) { 
     566                osync_error_set(error, OSYNC_ERROR_GENERIC, "Error inserting plugin directory into sys.path"); 
     567                PYERR_CLEAR(); 
     568                Py_DECREF(sys_module); 
     569                Py_DECREF(path); 
     570                Py_DECREF(plugindir); 
     571                return FALSE; 
     572        } 
     573         
     574        Py_DECREF(sys_module); 
     575        Py_DECREF(path); 
     576        Py_DECREF(plugindir); 
     577 
     578        return TRUE; 
     579} 
     580 
    530581osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error) 
    531582{ 
     
    536587         */ 
    537588 
    538         static PyThreadState *mainthread; 
    539  
    540         if (!Py_IsInitialized() || !PyEval_ThreadsInitialized()) { 
    541                 /* set python search path to look in our module directory first */ 
    542                 char *pypath = g_build_path(":", OPENSYNC_PYTHONPLG_DIR, getenv("PYTHONPATH"), NULL); 
    543                 if (pypath == NULL || setenv("PYTHONPATH", pypath, 1) != 0) { 
    544                         osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't set PYTHONPATH"); 
    545                         return FALSE; 
    546                 } 
    547                 g_free(pypath); 
    548  
    549                 /* init python */ 
     589        if (!Py_IsInitialized()) { 
     590                /* we're the first user of python in this process */ 
    550591                Py_InitializeEx(0); 
    551592                PyEval_InitThreads(); 
    552                 mainthread = PyThreadState_Get(); 
    553         } else { 
    554                 PyEval_AcquireThread(mainthread); 
    555         } 
     593        } else if (!PyEval_ThreadsInitialized()) { 
     594                /* The python interpreter has been initialised, but threads are not 
     595                 * I'm going to assume we're the only thread and continue, but this 
     596                 * is possibly unsafe! */ 
     597                PyEval_InitThreads(); 
     598        } 
     599 
     600        PyGILState_STATE pystate = PyGILState_Ensure(); 
     601        osync_bool ret = FALSE; 
     602 
     603        if (!set_search_path(error)) 
     604                goto out; 
    556605 
    557606        /* import opensync module */ 
    558607        PyObject *osync_module = pm_load_opensync(error);  
    559608        if (!osync_module) 
    560                 return FALSE
    561  
    562         osync_bool ret = scan_for_plugins(env, osync_module, error); 
     609                goto out
     610 
     611        ret = scan_for_plugins(env, osync_module, error); 
    563612        Py_DECREF(osync_module); 
    564613 
    565         PyEval_ReleaseThread(mainthread); 
     614out: 
     615        PyGILState_Release(pystate); 
     616 
    566617        return ret; 
    567618}