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:
|