LCOV - code coverage report
Current view: top level - util/python - pycppwrapper_util.h (source / functions) Hit Total Coverage
Test: report.info Lines: 38 43 88.4 %
Date: 2012-05-15 Functions: 18 20 90.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 18 60 30.0 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
       2                 :            : //
       3                 :            : // Permission to use, copy, modify, and/or distribute this software for any
       4                 :            : // purpose with or without fee is hereby granted, provided that the above
       5                 :            : // copyright notice and this permission notice appear in all copies.
       6                 :            : //
       7                 :            : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       8                 :            : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       9                 :            : // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
      10                 :            : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      11                 :            : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
      12                 :            : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      13                 :            : // PERFORMANCE OF THIS SOFTWARE.
      14                 :            : 
      15                 :            : #ifndef __PYCPPWRAPPER_UTIL_H
      16                 :            : #define __PYCPPWRAPPER_UTIL_H 1
      17                 :            : 
      18                 :            : #include <Python.h>
      19                 :            : 
      20                 :            : #include <exceptions/exceptions.h>
      21                 :            : 
      22                 :            : /**
      23                 :            :  * @file pycppwrapper_util.h
      24                 :            :  * @short Shared definitions for python/C(++) API
      25                 :            :  *
      26                 :            :  * This utility defines a set of convenient wrappers for the python C API
      27                 :            :  * to use it safely from our C++ bindings.  The python C API has many pitfalls
      28                 :            :  * such as not-so-consistent reference count policies.  Also, many existing
      29                 :            :  * examples are careless about error handling.  It's easy to find on the net
      30                 :            :  * example (even of "production use") python extensions like this:
      31                 :            :  *
      32                 :            :  * \code
      33                 :            :  *     new_exception = PyErr_NewException("mymodule.Exception", NULL, NULL);
      34                 :            :  *     // new_exception can be NULL, in which case the call to
      35                 :            :  *     // PyModule_AddObject will cause a surprising disruption.
      36                 :            :  *     PyModule_AddObject(mymodule, "Exception", new_exception); \endcode
      37                 :            :  *
      38                 :            :  * When using the python C API with C++, we should also be careful about
      39                 :            :  * exception safety.  The underlying C++ code (including standard C++ libraries
      40                 :            :  * and memory allocation) can throw exceptions, in which case we need to
      41                 :            :  * make sure any intermediate python objects are cleaned up (we also need to
      42                 :            :  * catch the C++ exceptions inside the binding and convert them to python
      43                 :            :  * errors, but that's a different subject).  This is not a trivial task
      44                 :            :  * because the python objects are represented as bare C pointers (so there's
      45                 :            :  * no destructor) and we need to address the exception safety along with python
      46                 :            :  * reference counters (so we cannot naively apply standard smart pointers).
      47                 :            :  *
      48                 :            :  * This utility tries to help address these issues.
      49                 :            :  *
      50                 :            :  * Also, it's intentional that this is a header-only utility.  This way the
      51                 :            :  * C++ loadable module won't depend on another C++ library (which is not
      52                 :            :  * necessarily wrong, but would increase management cost such as link-time
      53                 :            :  * troubles only for a small utility feature).
      54                 :            :  */
      55                 :            : 
      56                 :            : namespace isc {
      57                 :            : namespace util {
      58                 :            : namespace python {
      59                 :            : 
      60                 :            : /// This is thrown inside this utility when it finds a NULL pointer is passed
      61                 :            : /// when it should not be NULL.
      62                 :          2 : class PyCPPWrapperException : public isc::Exception {
      63                 :            : public:
      64                 :          0 :     PyCPPWrapperException(const char* file, size_t line, const char* what) :
      65 [ +  - ][ #  # ]:          3 :         isc::Exception(file, line, what) {}
         [ #  # ][ #  # ]
      66                 :            : };
      67                 :            : 
      68                 :            : /// This helper class is similar to the standard autoptr and manages PyObject
      69                 :            : /// using some kind of RAII techniques.  It is, however, customized for the
      70                 :            : /// python C API.
      71                 :            : ///
      72                 :            : /// A PyObjectContainer object is constructed with a pointer to PyObject,
      73                 :            : /// which is often just created dynamically.  The caller will eventually
      74                 :            : /// attach the object to a different python object (often a module or class)
      75                 :            : /// via specific methods or directly return it to the python interpreter.
      76                 :            : ///
      77                 :            : /// There are two cases in destructing the object: with or without decreasing
      78                 :            : /// a reference to the PyObject.  If the object is intended to be an argument
      79                 :            : /// to another python C library that increases the reference to the object for
      80                 :            : /// itself, we should normally release our own reference; otherwise the
      81                 :            : /// reference will leak and the object won't be garbage collected.  Also, when
      82                 :            : /// an unexpected error happens in the form of C++ exception, we should
      83                 :            : /// release the reference to prevent resource leak.
      84                 :            : ///
      85                 :            : /// In some other cases, we should simply give our reference to the caller.
      86                 :            : /// That is the case when the created object itself is a return value of
      87                 :            : /// an extended python method written in the C++ binding.  Likewise, some
      88                 :            : /// python C library functions "steal" the reference.  In these cases we
      89                 :            : /// should not decrease the reference; otherwise it would cause duplicate free.
      90                 :            : ///
      91                 :            : /// By default, the destructor of this class releases the reference to the
      92                 :            : /// PyObject.  If this behavior is desirable, you can extract the original
      93                 :            : /// bare pointer to the PyObject by the \c get() method.  If you don't want
      94                 :            : /// the reference to be decreased, the original bare pointer should be
      95                 :            : /// extracted using the \c release() method.
      96                 :            : ///
      97                 :            : /// In some other cases, it would be convenient if it's possible to create
      98                 :            : /// an "empty" container and reset it with a Python object later.
      99                 :            : /// For example, we may want to create a temporary Python object in the
     100                 :            : /// middle of a function and make sure that it's valid within the rest of
     101                 :            : /// the function scope, while we want to make sure its reference is released
     102                 :            : /// when the function returns (either normally or as a result of exception).
     103                 :            : /// To allow this scenario, this class defines the default constructor
     104                 :            : /// and the \c reset() method.  The default constructor allows the class
     105                 :            : /// object with an "empty" (NULL) Python object, while \c reset() allows
     106                 :            : /// the stored object to be replaced with a new one.  If there's a valid
     107                 :            : /// object was already set, \c reset() releases its reference.
     108                 :            : /// In general, it's safer to construct the container object with a valid
     109                 :            : /// Python object pointer.  The use of the default constructor and
     110                 :            : /// \c reset() should therefore be restricted to cases where it's
     111                 :            : /// absolutely necessary.
     112                 :            : ///
     113                 :            : /// There are two convenience methods for commonly used operations:
     114                 :            : /// \c installAsClassVariable() to add the PyObject as a class variable
     115                 :            : /// and \c installToModule to add the PyObject to a specified python module.
     116                 :            : /// These methods (at least to some extent) take care of the reference to
     117                 :            : /// the object (either release or keep) depending on the usage context so
     118                 :            : /// that the user don't have to worry about it.
     119                 :            : ///
     120                 :            : /// On construction, this class expects the pointer can be NULL.
     121                 :            : /// If it happens it immediately throws a \c PyCPPWrapperException exception.
     122                 :            : /// This behavior is to convert failures in the python C API (such as
     123                 :            : /// PyObject_New() returning NULL) to C++ exception so that we can unify
     124                 :            : /// error handling in the style of C++ exceptions.
     125                 :            : ///
     126                 :            : /// Examples 1: To create a tuple of two python objects, do this:
     127                 :            : ///
     128                 :            : /// \code
     129                 :            : ///     try {
     130                 :            : ///         PyObjectContainer container0(Py_BuildValue("I", 0));
     131                 :            : ///         PyObjectContainer container1(Py_BuildValue("s", cppobj.toText().c_str()));
     132                 :            : ///         return (Py_BuildValue("OO", container0.get(), container1.get()));
     133                 :            : ///     } catch { ... set python exception, etc ... } \endcode
     134                 :            : ///
     135                 :            : /// Commonly deployed buggy implementation to achieve this would be like this:
     136                 :            : /// \code
     137                 :            : ///    return (Py_BuildValue("OO", Py_BuildValue("I", 0),
     138                 :            : ///                          Py_BuildValue("s", cppobj.toText().c_str())));
     139                 :            : /// \endcode
     140                 :            : /// One clear bug of this code is that references to the element objects of
     141                 :            : /// the tuple will leak.
     142                 :            : /// (Assuming \c cppobj.toText() can throw) this code is also not exception
     143                 :            : /// safe; if \c cppobj.toText() throws the reference to the first object
     144                 :            : /// will leak, even if the code tried to do the necessary cleanup in the
     145                 :            : /// successful case.
     146                 :            : /// Further, this code naively passes the result of the first two calls to
     147                 :            : /// \c Py_BuildValue() to the third one even if they can be NULL.
     148                 :            : /// In this specific case, it happens to be okay because \c Py_BuildValue()
     149                 :            : /// accepts NULL and treats it as an indication of error.  But not all
     150                 :            : /// python C library works that way (remember, the API is so inconsistent)
     151                 :            : /// and we need to refer to the API manual every time we have to worry about
     152                 :            : /// passing a NULL object to a library function.  We'd certainly like to
     153                 :            : /// avoid such development overhead.  The code using \c PyObjectContainer
     154                 :            : /// addresses all these problems.
     155                 :            : ///
     156                 :            : /// Examples 2: Install a (constant) variable to a class.
     157                 :            : ///
     158                 :            : /// \code
     159                 :            : ///    try {
     160                 :            : ///        // installClassVariable is a wrapper of
     161                 :            : ///        // PyObjectContainer::installAsClassVariable.  See below.
     162                 :            : ///        installClassVariable(myclass_type, "SOME_CONSTANT",
     163                 :            : ///                             Py_BuildValue("I", 0));
     164                 :            : ///    } catch { ... }
     165                 :            : /// \endcode
     166                 :            : ///
     167                 :            : /// Examples 3: Install a custom exception to a module.
     168                 :            : ///
     169                 :            : /// \code
     170                 :            : ///    PyObject* new_exception; // publicly visible
     171                 :            : ///    ...
     172                 :            : ///    try {
     173                 :            : ///        new_exception = PyErr_NewException("mymodule.NewException",
     174                 :            : ///                                           NULL, NULL);
     175                 :            : ///        PyObjectContainer(new_exception).installToModule(mymodule,
     176                 :            : ///                                                         "NewException");
     177                 :            : ///    } catch { ... }
     178                 :            : /// \endcode
     179                 :            : ///
     180                 :            : /// Note that \c installToModule() keeps the reference to \c new_exception
     181                 :            : /// by default.  This is a common practice when we introduce a custom
     182                 :            : /// exception in a python biding written in C/C++.  See the code comment
     183                 :            : /// of the method for more details.
     184                 :            : struct PyObjectContainer {
     185                 :       3754 :     PyObjectContainer() : obj_(NULL) {}
     186                 :      22763 :     PyObjectContainer(PyObject* obj) : obj_(obj) {
     187         [ +  + ]:      22763 :         if (obj_ == NULL) {
     188 [ +  - ][ #  # ]:          2 :             isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
     189                 :            :                       "probably due to short memory");
     190                 :            :         }
     191                 :      22762 :     }
     192                 :      20240 :     ~PyObjectContainer() {
     193 [ +  + ][ #  # ]:      26694 :         if (obj_ != NULL) {
     194 [ +  + ][ #  # ]:      16333 :             Py_DECREF(obj_);
         [ #  # ][ #  # ]
                 [ #  # ]
     195                 :            :         }
     196                 :      20240 :     }
     197                 :       2214 :     void reset(PyObject* obj) {
     198         [ +  + ]:       2214 :         if (obj == NULL) {
     199 [ +  - ][ #  # ]:          2 :             isc_throw(PyCPPWrapperException, "Unexpected NULL PyObject, "
     200                 :            :                       "probably due to short memory");
     201                 :            :         }
     202         [ -  + ]:       2213 :         if (obj_ != NULL) {
     203         [ #  # ]:          0 :             Py_DECREF(obj_);
     204                 :            :         }
     205                 :       2213 :         obj_ = obj;
     206                 :       2213 :     }
     207                 :            :     PyObject* get() {
     208                 :      13665 :         return (obj_);
     209                 :            :     }
     210                 :            :     PyObject* release() {
     211                 :       8110 :         PyObject* ret = obj_;
     212                 :       8110 :         obj_ = NULL;
     213                 :            :         return (ret);
     214                 :            :     }
     215                 :            : 
     216                 :            :     // Install the enclosed PyObject to the specified python class 'pyclass'
     217                 :            :     // as a variable named 'name'.
     218                 :       4287 :     void installAsClassVariable(PyTypeObject& pyclass, const char* name) {
     219         [ -  + ]:       4287 :         if (PyDict_SetItemString(pyclass.tp_dict, name, obj_) < 0) {
     220 [ #  # ][ #  # ]:          0 :             isc_throw(PyCPPWrapperException, "Failed to set a class variable, "
     221                 :            :                       "probably due to short memory");
     222                 :            :         }
     223                 :            :         // Ownership successfully transferred to the class object.  We'll let
     224                 :            :         // it be released in the destructor.
     225                 :       4287 :     }
     226                 :            : 
     227                 :            :     // Install the enclosed PyObject to the specified module 'mod' as an
     228                 :            :     // object named 'name'.
     229                 :            :     // By default, this method explicitly keeps the reference to the object
     230                 :            :     // even after the module "steals" it.  To cancel this behavior and give
     231                 :            :     // the reference to the module completely, the third parameter 'keep_ref'
     232                 :            :     // should be set to false.
     233                 :        534 :     void installToModule(PyObject* mod, const char* name,
     234                 :            :                          bool keep_ref = true)
     235                 :            :     {
     236         [ -  + ]:        534 :         if (PyModule_AddObject(mod, name, obj_) < 0) {
     237 [ #  # ][ #  # ]:          0 :             isc_throw(PyCPPWrapperException, "Failed to add an object to "
     238                 :            :                       "module, probably due to short memory");
     239                 :            :         }
     240                 :            :         // PyModule_AddObject has "stolen" the reference, so unless we
     241                 :            :         // have to retain it ourselves we don't (shouldn't) decrease it.
     242                 :            :         // However, we actually often need to keep our own reference because
     243                 :            :         // objects added to a module are often referenced via non local
     244                 :            :         // C/C++ variables in various places of the C/C++ code.  In order
     245                 :            :         // for the code to run safely even if some buggy/evil python program
     246                 :            :         // performs 'del mod.obj', we need the extra reference.  See, e.g.:
     247                 :            :         // http://docs.python.org/py3k/c-api/init.html#Py_Initialize
     248                 :            :         // http://mail.python.org/pipermail/python-dev/2005-June/054238.html
     249         [ +  + ]:        534 :         if (keep_ref) {
     250                 :        525 :             Py_INCREF(obj_);
     251                 :            :         }
     252                 :        534 :         obj_ = NULL;
     253                 :        534 :     }
     254                 :            : 
     255                 :            : protected:
     256                 :            :     PyObject* obj_;
     257                 :            : };
     258                 :            : 
     259                 :            : /// This templated class is a derived class of \c PyObjectContainer and
     260                 :            : /// manages C++-class based python objects.
     261                 :            : ///
     262                 :            : /// The template parameter \c PYSTRUCT must be a derived class (structure) of
     263                 :            : /// \c PyObject that has a member variable named \c cppobj, which must be a
     264                 :            : /// a pointer to \c CPPCLASS (the second template parameter).
     265                 :            : ///
     266                 :            : /// For example, to define a custom python class based on a C++ class, MyClass,
     267                 :            : /// we'd define a class (struct) named \c s_MyClass like this:
     268                 :            : /// \code
     269                 :            : ///    class s_MyClass : public PyObject {
     270                 :            : ///    public:
     271                 :            : ///       s_MyClass() : cppobj(NULL) {}
     272                 :            : ///       MyClass* cppobj;
     273                 :            : ///    };
     274                 :            : /// \endcode
     275                 :            : ///
     276                 :            : /// And, to build and return a python version of MyClass object, write the
     277                 :            : /// following C++ code:
     278                 :            : /// \code
     279                 :            : ///    typedef CPPPyObjectContainer<s_MyClass, MyClass> MyContainer;
     280                 :            : ///    try {
     281                 :            : ///        // below, myclass_type is of \c PyTypeObject that defines
     282                 :            : ///        // a python class (type) for MyClass
     283                 :            : ///        MyContainer container(PyObject_New(s_MyClass, myclass_type));
     284                 :            : ///        container.set(new MyClass());
     285                 :            : ///        return (container.release()); // give the reference to the caller
     286                 :            : ///    } catch { ... }
     287                 :            : /// \endcode
     288                 :            : ///
     289                 :            : /// This code prevents bugs like NULL pointer dereference when \c PyObject_New
     290                 :            : /// fails or resource leaks when new'ing \c MyClass results in an exception.
     291                 :            : /// Note that we use \c release() (derived from the base class) instead of
     292                 :            : /// \c get(); in this case we should simply pass the reference generated in
     293                 :            : /// \c PyObject_New() to the caller.
     294                 :            : template <typename PYSTRUCT, typename CPPCLASS>
     295                 :        213 : struct CPPPyObjectContainer : public PyObjectContainer {
     296                 :       6625 :     explicit CPPPyObjectContainer(PYSTRUCT* obj) : PyObjectContainer(obj) {}
     297                 :            : 
     298                 :            :     // This method associates a C++ object with the corresponding python
     299                 :            :     // object enclosed in this class.
     300                 :       6625 :     void set(CPPCLASS* value) {
     301         [ -  + ]:       6625 :         if (value == NULL) {
     302 [ #  # ][ #  # ]:          0 :             isc_throw(PyCPPWrapperException, "Unexpected NULL C++ object, "
     303                 :            :                       "probably due to short memory");
     304                 :            :         }
     305                 :       6625 :         static_cast<PYSTRUCT*>(obj_)->cppobj = value;
     306                 :       6625 :     }
     307                 :            : 
     308                 :            :     // This is a convenience short cut to associate a C++ object with the
     309                 :            :     // python object and install it to the specified python class \c pyclass
     310                 :            :     // as a variable named \c name.
     311                 :            :     void installAsClassVariable(PyTypeObject& pyclass, const char* name,
     312                 :            :                                 CPPCLASS* value)
     313                 :            :     {
     314                 :            :         set(value);
     315                 :            :         PyObjectContainer::installAsClassVariable(pyclass, name);
     316                 :            :     }
     317                 :            : };
     318                 :            : 
     319                 :            : /// A shortcut function to install a python class variable.
     320                 :            : ///
     321                 :            : /// It installs a python object \c obj to a specified class \c pyclass
     322                 :            : /// as a variable named \c name.
     323                 :            : inline void
     324                 :       4287 : installClassVariable(PyTypeObject& pyclass, const char* name, PyObject* obj) {
     325         [ +  - ]:       4287 :     PyObjectContainer(obj).installAsClassVariable(pyclass, name);
     326                 :       4287 : }
     327                 :            : 
     328                 :            : } // namespace python
     329                 :            : } // namespace util
     330                 :            : } // namespace isc
     331                 :            : #endif // __PYCPPWRAPPER_UTIL_H
     332                 :            : 
     333                 :            : // Local Variables:
     334                 :            : // mode: c++
     335                 :            : // End:

Generated by: LCOV version 1.9