LCOV - code coverage report
Current view: top level - nnstreamer-2.4.2/ext/nnstreamer/extra - nnstreamer_python3_helper.cc (source / functions) Coverage Total Hit
Test: nnstreamer 2.4.2-0 nnstreamer/nnstreamer#eca68b8d050408568af95d831a8eef62aaee7784 Lines: 66.5 % 257 171
Test Date: 2025-03-14 05:36:58 Functions: 100.0 % 18 18

            Line data    Source code
       1              : /* SPDX-License-Identifier: LGPL-2.1-only */
       2              : /**
       3              :  * @file        nnstreamer_python3_helper.cc
       4              :  * @date        10 Apr 2019
       5              :  * @brief       python helper structure for nnstreamer tensor_filter
       6              :  * @see         http://github.com/nnstreamer/nnstreamer
       7              :  * @author      Dongju Chae <dongju.chae@samsung.com>
       8              :  * @bug         No known bugs except for NYI items
       9              :  *
      10              :  * A python module that provides a wrapper for internal structures in tensor_filter_python.
      11              :  * Users can import this module to access such a functionality
      12              :  *
      13              :  * -- Example python script
      14              :  *  import numpy as np
      15              :  *  import nnstreamer_python as nns
      16              :  *  dim = nns.TensorShape([1,2,3], np.uint8)
      17              :  */
      18              : 
      19              : #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
      20              : #pragma GCC diagnostic ignored "-Wformat"
      21              : #endif
      22              : 
      23              : #include <nnstreamer_util.h>
      24              : #include "nnstreamer_python3_helper.h"
      25              : 
      26              : #ifdef __cplusplus
      27              : extern "C" {
      28              : #endif
      29              : 
      30              : /** @brief object structure for custom Python type: TensorShape */
      31              : typedef struct {
      32              :   PyObject_HEAD PyObject *dims;
      33              :   PyArray_Descr *type;
      34              : } TensorShapeObject;
      35              : 
      36              : /** @brief define a prototype for this python module */
      37              : PyMODINIT_FUNC PyInit_nnstreamer_python (void);
      38              : 
      39              : /**
      40              :  * @brief method impl. for setDims
      41              :  * @param self : Python type object
      42              :  * @param args : arguments for the method
      43              :  */
      44              : static PyObject *
      45          122 : TensorShape_setDims (TensorShapeObject *self, PyObject *args)
      46              : {
      47              :   PyObject *dims;
      48              :   PyObject *new_dims;
      49              :   Py_ssize_t i, len;
      50              : 
      51              :   /** PyArg_ParseTuple() returns borrowed references */
      52          122 :   if (!PyArg_ParseTuple (args, "O", &dims))
      53            0 :     Py_RETURN_NONE;
      54              : 
      55          122 :   len = PyList_Size (dims);
      56          122 :   if (len < 0 || len > NNS_TENSOR_RANK_LIMIT)
      57            0 :     Py_RETURN_NONE;
      58              : 
      59          122 :   if (len < NNS_TENSOR_RANK_LIMIT) {
      60          205 :     for (i = 0; i < NNS_TENSOR_RANK_LIMIT - len; i++)
      61          190 :       PyList_Append (dims, PyLong_FromLong (0));
      62           15 :     new_dims = dims;
      63           15 :     Py_XINCREF (new_dims);
      64              :   } else {
      65              :     /** PyList_GetSlice() returns new reference */
      66          107 :     new_dims = PyList_GetSlice (dims, 0, NNS_TENSOR_RANK_LIMIT);
      67              :   }
      68              : 
      69              :   /** swap 'self->dims' */
      70          122 :   Py_SAFEDECREF (self->dims);
      71          122 :   self->dims = new_dims;
      72              : 
      73          122 :   Py_RETURN_NONE;
      74              : }
      75              : 
      76              : /**
      77              :  * @brief method impl. for getDims
      78              :  * @param self : Python type object
      79              :  * @param args : arguments for the method
      80              :  */
      81              : static PyObject *
      82          132 : TensorShape_getDims (TensorShapeObject *self, PyObject *args)
      83              : {
      84              :   UNUSED (args);
      85          132 :   return Py_BuildValue ("O", self->dims);
      86              : }
      87              : 
      88              : /**
      89              :  * @brief method impl. for getType
      90              :  * @param self : Python type object
      91              :  * @param args : arguments for the method
      92              :  */
      93              : static PyObject *
      94          118 : TensorShape_getType (TensorShapeObject *self, PyObject *args)
      95              : {
      96              :   UNUSED (args);
      97          118 :   return Py_BuildValue ("O", self->type);
      98              : }
      99              : 
     100              : /**
     101              :  * @brief new callback for custom type object
     102              :  * @param self : Python type object
     103              :  * @param args : arguments for the method
     104              :  * @param kw : keywords for the arguments
     105              :  */
     106              : static PyObject *
     107          122 : TensorShape_new (PyTypeObject *type, PyObject *args, PyObject *kw)
     108              : {
     109          122 :   TensorShapeObject *self = (TensorShapeObject *) type->tp_alloc (type, 0);
     110              :   UNUSED (args);
     111              :   UNUSED (kw);
     112              : 
     113          122 :   g_assert (self);
     114              : 
     115              :   /** Assign default values */
     116          122 :   self->dims = PyList_New (0);
     117          122 :   self->type = PyArray_DescrFromType (NPY_UINT8);
     118          122 :   Py_XINCREF (self->type);
     119              : 
     120          122 :   return (PyObject *) self;
     121              : }
     122              : 
     123              : /**
     124              :  * @brief init callback for custom type object
     125              :  * @param self : Python type object
     126              :  * @param args : arguments for the method
     127              :  * @param kw : keywords for the arguments
     128              :  */
     129              : static int
     130          122 : TensorShape_init (TensorShapeObject *self, PyObject *args, PyObject *kw)
     131              : {
     132          122 :   char *keywords[] = { (char *) "dims", (char *) "type", NULL };
     133          122 :   PyObject *dims = NULL;
     134          122 :   PyObject *type = NULL;
     135              : 
     136          122 :   if (!PyArg_ParseTupleAndKeywords (args, kw, "|OO", keywords, &dims, &type))
     137            0 :     return -1;
     138              : 
     139          122 :   if (dims) {
     140          122 :     PyObject *none = PyObject_CallMethod (
     141              :         (PyObject *) self, (char *) "setDims", (char *) "O", dims);
     142          122 :     Py_SAFEDECREF (none);
     143              :   }
     144              : 
     145          122 :   if (type) {
     146              :     PyArray_Descr *dtype;
     147          122 :     if (PyArray_DescrConverter (type, &dtype) != NPY_FAIL) {
     148              :       /** swap 'self->type' */
     149          122 :       Py_SAFEDECREF (self->type);
     150          122 :       self->type = dtype;
     151          122 :       Py_XINCREF (self->type);
     152              :     } else
     153            0 :       Py_ERRMSG ("Wrong data type.");
     154              :   }
     155              : 
     156          122 :   return 0;
     157              : }
     158              : 
     159              : /**
     160              :  * @brief dealloc callback for custom type object
     161              :  * @param self : Python type object
     162              :  */
     163              : static void
     164           78 : TensorShape_dealloc (TensorShapeObject *self)
     165              : {
     166           78 :   Py_SAFEDECREF (self->dims);
     167           78 :   Py_SAFEDECREF (self->type);
     168           78 :   Py_TYPE (self)->tp_free ((PyObject *) self);
     169           78 : }
     170              : 
     171              : /** @brief members for custom type object */
     172              : static PyMemberDef TensorShape_members[]
     173              :     = { { (char *) "dims", T_OBJECT_EX, offsetof (TensorShapeObject, dims), 0, NULL },
     174              :         { (char *) "type", T_OBJECT_EX, offsetof (TensorShapeObject, type), 0, NULL } };
     175              : 
     176              : /** @brief methods for custom type object */
     177              : static PyMethodDef TensorShape_methods[] = { { (char *) "setDims", (PyCFunction) TensorShape_setDims,
     178              :                                                  METH_VARARGS | METH_KEYWORDS, NULL },
     179              :   { (char *) "getDims", (PyCFunction) TensorShape_getDims, METH_VARARGS | METH_KEYWORDS, NULL },
     180              :   { (char *) "getType", (PyCFunction) TensorShape_getType, METH_VARARGS | METH_KEYWORDS, NULL },
     181              :   { NULL, NULL, 0, NULL } };
     182              : 
     183              : /** @brief Structure for custom type object */
     184          541 : static PyTypeObject TensorShapeType = [] {
     185              : #pragma GCC diagnostic push
     186              : #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
     187          541 :   PyTypeObject ret = { PyVarObject_HEAD_INIT (NULL, 0) };
     188              : #pragma GCC diagnostic pop
     189          541 :   ret.tp_name = "nnstreamer_python.TensorShape";
     190          541 :   ret.tp_basicsize = sizeof (TensorShapeObject);
     191          541 :   ret.tp_itemsize = 0;
     192          541 :   ret.tp_dealloc = (destructor) TensorShape_dealloc;
     193          541 :   ret.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
     194          541 :   ret.tp_doc = "TensorShape type";
     195          541 :   ret.tp_methods = TensorShape_methods;
     196          541 :   ret.tp_members = TensorShape_members;
     197          541 :   ret.tp_init = (initproc) TensorShape_init;
     198          541 :   ret.tp_new = TensorShape_new;
     199          541 :   return ret;
     200              : }();
     201              : 
     202              : #pragma GCC diagnostic push
     203              : #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
     204              : static PyModuleDef nnstreamer_python_module
     205              :     = { PyModuleDef_HEAD_INIT, "nnstreamer_python", NULL, -1, NULL };
     206              : #pragma GCC diagnostic pop
     207              : 
     208              : /** @brief module initialization (python 3.x) */
     209              : PyMODINIT_FUNC
     210           21 : PyInit_nnstreamer_python (void)
     211              : {
     212           21 :   PyObject *type_object = (PyObject *) &TensorShapeType;
     213              :   PyObject *module;
     214              : 
     215              :   /** Check TensorShape type */
     216           21 :   if (PyType_Ready (&TensorShapeType) < 0)
     217            0 :     return NULL;
     218              : 
     219           21 :   module = PyModule_Create (&nnstreamer_python_module);
     220           21 :   if (module == NULL)
     221            0 :     return NULL;
     222              : 
     223              :   /** For numpy array init. */
     224           21 :   import_array ();
     225              : 
     226              :   Py_INCREF (type_object);
     227           21 :   PyModule_AddObject (module, "TensorShape", type_object);
     228              : 
     229           21 :   return module;
     230              : }
     231              : 
     232              : /**
     233              :  * @brief       return the data type of the tensor
     234              :  * @param npyType       : the defined type of Python numpy
     235              :  * @return the enum of defined _NNS_TYPE
     236              :  */
     237              : tensor_type
     238           64 : getTensorType (NPY_TYPES npyType)
     239              : {
     240           64 :   switch (npyType) {
     241            3 :     case NPY_INT32:
     242            3 :       return _NNS_INT32;
     243            0 :     case NPY_UINT32:
     244            0 :       return _NNS_UINT32;
     245            4 :     case NPY_INT16:
     246            4 :       return _NNS_INT16;
     247            1 :     case NPY_UINT16:
     248            1 :       return _NNS_UINT16;
     249            0 :     case NPY_INT8:
     250            0 :       return _NNS_INT8;
     251           56 :     case NPY_UINT8:
     252           56 :       return _NNS_UINT8;
     253            0 :     case NPY_INT64:
     254            0 :       return _NNS_INT64;
     255            0 :     case NPY_UINT64:
     256            0 :       return _NNS_UINT64;
     257            0 :     case NPY_FLOAT32:
     258            0 :       return _NNS_FLOAT32;
     259            0 :     case NPY_FLOAT64:
     260            0 :       return _NNS_FLOAT64;
     261            0 :     default:
     262              :       /** @todo Support other types */
     263            0 :       break;
     264              :   }
     265              : 
     266            0 :   return _NNS_END;
     267              : }
     268              : 
     269              : /**
     270              :  * @brief       return the data type of the tensor for Python numpy
     271              :  * @param tType : the defined type of NNStreamer
     272              :  * @return the enum of defined numpy datatypes
     273              :  */
     274              : NPY_TYPES
     275           95 : getNumpyType (tensor_type tType)
     276              : {
     277           95 :   switch (tType) {
     278            0 :     case _NNS_INT32:
     279            0 :       return NPY_INT32;
     280            0 :     case _NNS_UINT32:
     281            0 :       return NPY_UINT32;
     282            2 :     case _NNS_INT16:
     283            2 :       return NPY_INT16;
     284            2 :     case _NNS_UINT16:
     285            2 :       return NPY_UINT16;
     286            0 :     case _NNS_INT8:
     287            0 :       return NPY_INT8;
     288           91 :     case _NNS_UINT8:
     289           91 :       return NPY_UINT8;
     290            0 :     case _NNS_INT64:
     291            0 :       return NPY_INT64;
     292            0 :     case _NNS_UINT64:
     293            0 :       return NPY_UINT64;
     294            0 :     case _NNS_FLOAT32:
     295            0 :       return NPY_FLOAT32;
     296            0 :     case _NNS_FLOAT64:
     297            0 :       return NPY_FLOAT64;
     298            0 :     default:
     299              :       /** @todo Support other types */
     300            0 :       break;
     301              :   }
     302            0 :   return NPY_NOTYPE;
     303              : }
     304              : 
     305              : /**
     306              :  * @brief       load the python script
     307              :  */
     308              : int
     309           30 : loadScript (PyObject **core_obj, const gchar *module_name, const gchar *class_name)
     310              : {
     311           30 :   PyObject *module = PyImport_ImportModule (module_name);
     312              : 
     313           30 :   if (module) {
     314           24 :     PyObject *cls = PyObject_GetAttrString (module, class_name);
     315           24 :     Py_SAFEDECREF (module);
     316              : 
     317           24 :     if (cls) {
     318           23 :       *core_obj = PyObject_CallObject (cls, NULL);
     319           23 :       Py_SAFEDECREF (cls);
     320              :     } else {
     321            1 :       Py_ERRMSG ("Cannot find '%s' class in the script.\n", class_name);
     322            1 :       return -2;
     323              :     }
     324              :   } else {
     325            6 :     Py_ERRMSG ("The script (%s) is not properly loaded.\n", module_name);
     326            6 :     return -1;
     327              :   }
     328              : 
     329           23 :   return 0;
     330              : }
     331              : 
     332              : /**
     333              :  * @brief       loads the dynamic shared object of the python
     334              :  */
     335              : int
     336           43 : openPythonLib (void **handle)
     337              : {
     338              :   /**
     339              :    * To fix import error of python extension modules
     340              :    * (e.g., multiarray.x86_64-linux-gnu.so: undefined symbol: PyExc_SystemError)
     341              :    */
     342           43 :   gchar libname[32] = {
     343              :     0,
     344              :   };
     345              : 
     346           43 :   g_snprintf (libname, sizeof (libname), "libpython%d.%d.%s", PY_MAJOR_VERSION,
     347              :       PY_MINOR_VERSION, SO_EXT);
     348           43 :   *handle = dlopen (libname, RTLD_LAZY | RTLD_GLOBAL);
     349           43 :   if (NULL == *handle) {
     350              :     /* check the python was compiled with '--with-pymalloc' */
     351            0 :     g_snprintf (libname, sizeof (libname), "libpython%d.%dm.%s",
     352              :         PY_MAJOR_VERSION, PY_MINOR_VERSION, SO_EXT);
     353              : 
     354            0 :     *handle = dlopen (libname, RTLD_LAZY | RTLD_GLOBAL);
     355            0 :     if (NULL == *handle)
     356            0 :       return -1;
     357              :   }
     358              : 
     359           43 :   return 0;
     360              : }
     361              : 
     362              : /**
     363              :  * @brief       Add custom python module to system path
     364              :  */
     365              : int
     366           43 : addToSysPath (const gchar *path)
     367              : {
     368              :   /** Add current/directory path to sys.path */
     369           43 :   PyObject *sys_module = PyImport_ImportModule ("sys");
     370           43 :   if (nullptr == sys_module) {
     371            0 :     Py_ERRMSG ("Cannot import python module 'sys'.");
     372            0 :     return -1;
     373              :   }
     374              : 
     375           43 :   PyObject *sys_path = PyObject_GetAttrString (sys_module, "path");
     376           43 :   if (nullptr == sys_path) {
     377            0 :     Py_ERRMSG ("Cannot import python module 'path'.");
     378            0 :     Py_SAFEDECREF (sys_module);
     379            0 :     return -1;
     380              :   }
     381              : 
     382           43 :   PyList_Append (sys_path, PyUnicode_FromString ("."));
     383           43 :   PyList_Append (sys_path, PyUnicode_FromString (path));
     384              : 
     385           43 :   Py_SAFEDECREF (sys_path);
     386           43 :   Py_SAFEDECREF (sys_module);
     387              : 
     388           43 :   return 0;
     389              : }
     390              : 
     391              : /**
     392              :  * @brief parse the converting result to feed output tensors
     393              :  * @param[result] Python object returned by convert
     394              :  * @param[info] info Structure for output tensors info
     395              :  * @return 0 if no error, otherwise negative errno
     396              :  */
     397              : int
     398           64 : parseTensorsInfo (PyObject *result, GstTensorsInfo *info)
     399              : {
     400           64 :   Py_ssize_t i, j, num = PyList_Size (result);
     401              : 
     402           64 :   if (num < 0 || num > NNS_TENSOR_SIZE_LIMIT)
     403           22 :     return -1;
     404              : 
     405           42 :   gst_tensors_info_init (info);
     406           42 :   info->num_tensors = (unsigned int) num;
     407          106 :   for (i = 0; i < num; i++) {
     408           64 :     GstTensorInfo *_info = gst_tensors_info_get_nth_info (info, (guint) i);
     409              : 
     410              :     /** don't own the reference */
     411           64 :     PyObject *tensor_shape = PyList_GetItem (result, (Py_ssize_t) i);
     412           64 :     if (nullptr == tensor_shape) {
     413            0 :       Py_ERRMSG ("The function %s has failed, cannot get TensorShape object.", __FUNCTION__);
     414            0 :       return -1;
     415              :     }
     416              : 
     417           64 :     PyObject *shape_type = PyObject_CallMethod (tensor_shape, (char *) "getType", NULL);
     418           64 :     if (nullptr == shape_type) {
     419            0 :       Py_ERRMSG ("The function %s has failed, cannot get the tensor type.", __FUNCTION__);
     420            0 :       return -1;
     421              :     }
     422              : 
     423              :     /** convert numpy type to tensor type */
     424           64 :     _info->type = getTensorType ((NPY_TYPES) (((PyArray_Descr *) shape_type)->type_num));
     425           64 :     Py_SAFEDECREF (shape_type);
     426              : 
     427           64 :     PyObject *shape_dims = PyObject_CallMethod (tensor_shape, (char *) "getDims", NULL);
     428           64 :     if (nullptr == shape_dims) {
     429            0 :       Py_ERRMSG ("The function %s has failed, cannot get the tensor dimension.", __FUNCTION__);
     430            0 :       return -1;
     431              :     }
     432              : 
     433           64 :     if (!PyList_CheckExact (shape_dims)) {
     434            0 :       Py_ERRMSG ("The function %s has failed, dimension should be a list.", __FUNCTION__);
     435            0 :       Py_SAFEDECREF (shape_dims);
     436            0 :       return -EINVAL;
     437              :     }
     438              : 
     439           64 :     Py_ssize_t rank = PyList_Size (shape_dims);
     440           64 :     if (rank < 0 || rank > NNS_TENSOR_RANK_LIMIT) {
     441            0 :       Py_ERRMSG ("The function %s has failed, max rank of tensor dimension is %d.",
     442              :           __FUNCTION__, NNS_TENSOR_RANK_LIMIT);
     443            0 :       Py_SAFEDECREF (shape_dims);
     444            0 :       return -EINVAL;
     445              :     }
     446              : 
     447         1088 :     for (j = 0; j < rank; j++) {
     448         1024 :       PyErr_Clear ();
     449         1024 :       PyObject *item = PyList_GetItem (shape_dims, (Py_ssize_t) j);
     450         1024 :       int val = -1;
     451              : 
     452         1024 :       if (PyErr_Occurred ()) {
     453            0 :         PyErr_Print ();
     454            0 :         PyErr_Clear ();
     455            0 :         _info->dimension[j] = 0;
     456            0 :         Py_ERRMSG ("Python nnstreamer plugin has returned dimensions of the %zd'th tensor not in an array. Python code should return int-type array for dimensions. Indexes are counted from 0.\n",
     457              :             i + 1);
     458            0 :         Py_SAFEDECREF (shape_dims);
     459            0 :         return -EINVAL;
     460              :       }
     461              : 
     462         1024 :       if (PyLong_Check (item)) {
     463         1021 :         val = (int) PyLong_AsLong (item);
     464            3 :       } else if (PyFloat_Check (item)) {
     465              :         /** Regard this as a warning. Don't return -EINVAL with this */
     466            3 :         val = (int) PyFloat_AsDouble (item);
     467            3 :         Py_ERRMSG ("Python nnstreamer plugin has returned the %zd'th dimension value of the %zd'th tensor in floating-point type (%f), which is casted as unsigned-int. Python code should return int-type for dimension values. Indexes are counted from 0.\n",
     468              :             j + 1, i + 1, PyFloat_AsDouble (item));
     469              :       } else {
     470            0 :         _info->dimension[j] = 0;
     471            0 :         Py_ERRMSG ("Python nnstreamer plugin has returned the %zd'th dimension value of the %zd'th tensor neither in integer or floating-pointer. Python code should return int-type for dimension values. Indexes are counted from 0.\n",
     472              :             j + 1, i + 1);
     473            0 :         Py_SAFEDECREF (shape_dims);
     474            0 :         return -EINVAL;
     475              :       }
     476              : 
     477         1024 :       if (val < 0) {
     478            0 :         Py_ERRMSG ("The %zd'th dimension value of the %zd'th tensor is invalid (%d).",
     479              :             j + 1, i + 1, val);
     480            0 :         Py_SAFEDECREF (shape_dims);
     481            0 :         return -EINVAL;
     482              :       }
     483              : 
     484         1024 :       _info->dimension[j] = (uint32_t) val;
     485              :     }
     486              : 
     487           64 :     _info->name = NULL;
     488           64 :     Py_SAFEDECREF (shape_dims);
     489              :   }
     490              : 
     491              :   /* Validate output tensors info after parsing python object. */
     492           42 :   if (!gst_tensors_info_validate (info)) {
     493            0 :     gchar *info_str = gst_tensors_info_to_string (info);
     494            0 :     Py_ERRMSG ("Failed to parse the tensors information from python script, it may include invalid tensor type or dimension value (%s).",
     495              :         info_str);
     496            0 :     g_free (info_str);
     497            0 :     return -EINVAL;
     498              :   }
     499              : 
     500           42 :   return 0;
     501              : }
     502              : 
     503              : /**
     504              :  * @brief       allocate TensorShape object
     505              :  * @param info : the tensor info
     506              :  * @return created object
     507              :  */
     508              : PyObject *
     509           52 : PyTensorShape_New (PyObject *shape_cls, const GstTensorInfo *info)
     510              : {
     511           52 :   _import_array (); /** for numpy */
     512              : 
     513           52 :   PyObject *args = PyTuple_New (2);
     514           52 :   PyObject *dims = PyList_New (NNS_TENSOR_RANK_LIMIT);
     515           52 :   PyObject *type = (PyObject *) PyArray_DescrFromType (getNumpyType (info->type));
     516              : 
     517           52 :   if (nullptr == args || nullptr == dims || nullptr == type) {
     518            0 :     Py_ERRMSG ("The function %s has failed, cannot create args.", __FUNCTION__);
     519            0 :     PyErr_Clear ();
     520            0 :     Py_SAFEDECREF (args);
     521            0 :     Py_SAFEDECREF (dims);
     522            0 :     Py_SAFEDECREF (type);
     523            0 :     return nullptr;
     524              :   }
     525              : 
     526          884 :   for (int i = 0; i < NNS_TENSOR_RANK_LIMIT; i++)
     527          832 :     PyList_SetItem (dims, i, PyLong_FromLong ((uint64_t) info->dimension[i]));
     528              : 
     529           52 :   PyTuple_SetItem (args, 0, dims);
     530           52 :   PyTuple_SetItem (args, 1, type);
     531              : 
     532           52 :   return PyObject_CallObject (shape_cls, args);
     533              :   /* Its value is checked by setInputTensorDim */
     534              : }
     535              : 
     536              : static int python_init_counter = 0;
     537              : static PyThreadState *st = NULL;
     538              : /**
     539              :  * @brief Py_Initialize common wrapper for Python subplugins
     540              :  * @note This prevents a python-using subplugin finalizing another subplugin's
     541              :  * python interpreter by sharing the reference counter.
     542              :  */
     543              : void
     544          486 : nnstreamer_python_init_refcnt ()
     545              : {
     546          486 :   if (!Py_IsInitialized ()) {
     547          474 :     Py_Initialize ();
     548              :     PyEval_InitThreads_IfGood ();
     549          474 :     st = PyEval_SaveThread ();
     550              :   }
     551          486 :   python_init_counter++;
     552          486 : }
     553              : 
     554              : /**
     555              :  * @brief Py_Finalize common wrapper for Python subplugins
     556              :  * @note This prevents a python-using subplugin finalizing another subplugin's
     557              :  * python interpreter by sharing the reference counter.
     558              :  */
     559              : void
     560          486 : nnstreamer_python_fini_refcnt ()
     561              : {
     562          486 :   python_init_counter--;
     563          486 :   if (python_init_counter == 0) {
     564              :     /**
     565              :      * @todo Python Finalize() is buggy and leaky.
     566              :      *   Do not call it until Python is fixed. (not fixed as in 2023-12)
     567              :      *   Calling Finalize at this state will leave modules randomly, which
     568              :      *   may cause assertion failure at exit.
     569              :       PyEval_RestoreThread (st);
     570              :       Py_Finalize ();
     571              :      */
     572              :   }
     573          486 : }
     574              : 
     575              : /**
     576              :  * @brief Check Py_Init status for python eval functions.
     577              :  * @return 0 if it's ready. negative error value if it's not ready.
     578              :  */
     579              : int
     580          486 : nnstreamer_python_status_check ()
     581              : {
     582          486 :   if (python_init_counter == 0) {
     583            0 :     fprintf (stderr, "nnstreamer_python_init_refcnt() is not called or it's already closed.");
     584            0 :     return -EINVAL;
     585              :   }
     586              : 
     587          486 :   if (!Py_IsInitialized ()) {
     588            0 :     fprintf (stderr, "Py_IsInitialized () is FALSE. If nnstreamer is called by python context, please ignore this error.");
     589            0 :     return -EINVAL;
     590              :   }
     591          486 :   return 0;
     592              : }
     593              : 
     594              : #ifdef __cplusplus
     595              : } /* extern "C" */
     596              : #endif
        

Generated by: LCOV version 2.0-1