pytalloc: Add pytalloc_BaseObject_PyType_Ready() wrapper
[samba.git] / lib / talloc / pytalloc_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Python/Talloc glue
4    Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <Python.h>
21 #include "replace.h"
22 #include <talloc.h>
23 #include "pytalloc.h"
24 #include <assert.h>
25 #include "pytalloc_private.h"
26
27 _PUBLIC_ PyTypeObject *pytalloc_GetObjectType(void)
28 {
29         static PyTypeObject *type = NULL;
30         PyObject *mod;
31
32         if (type != NULL) {
33                 return type;
34         }
35
36         mod = PyImport_ImportModule("talloc");
37         if (mod == NULL) {
38                 return NULL;
39         }
40
41         type = (PyTypeObject *)PyObject_GetAttrString(mod, "Object");
42         Py_DECREF(mod);
43
44         return type;
45 }
46
47 _PUBLIC_ PyTypeObject *pytalloc_GetBaseObjectType(void)
48 {
49         static PyTypeObject *type = NULL;
50         PyObject *mod;
51
52         if (type != NULL) {
53                 return type;
54         }
55
56         mod = PyImport_ImportModule("talloc");
57         if (mod == NULL) {
58                 return NULL;
59         }
60
61         type = (PyTypeObject *)PyObject_GetAttrString(mod, "BaseObject");
62         Py_DECREF(mod);
63
64         return type;
65 }
66
67 /**
68  * Import an existing talloc pointer into a Python object.
69  */
70 _PUBLIC_ PyObject *pytalloc_steal_ex(PyTypeObject *py_type, TALLOC_CTX *mem_ctx,
71                                      void *ptr)
72 {
73         PyTypeObject *BaseObjectType = pytalloc_GetBaseObjectType();
74         PyTypeObject *ObjectType = pytalloc_GetObjectType();
75
76         if (mem_ctx == NULL) {
77                 return PyErr_NoMemory();
78         }
79
80         if (PyType_IsSubtype(py_type, BaseObjectType)) {
81                 pytalloc_BaseObject *ret
82                         = (pytalloc_BaseObject *)py_type->tp_alloc(py_type, 0);
83
84                 ret->talloc_ctx = talloc_new(NULL);
85                 if (ret->talloc_ctx == NULL) {
86                         return NULL;
87                 }
88
89                 /*
90                  * This allows us to keep multiple references to this object -
91                  * we only reference this context, which is per ptr, not the
92                  * talloc_ctx, which is per pytalloc_Object
93                  */
94                 if (talloc_steal(ret->talloc_ctx, mem_ctx) == NULL) {
95                         return NULL;
96                 }
97                 ret->talloc_ptr_ctx = mem_ctx;
98                 talloc_set_name_const(ret->talloc_ctx, py_type->tp_name);
99                 ret->ptr = ptr;
100                 return (PyObject *)ret;
101
102         } else if (PyType_IsSubtype(py_type, ObjectType)) {
103                 pytalloc_Object *ret
104                         = (pytalloc_Object *)py_type->tp_alloc(py_type, 0);
105
106                 ret->talloc_ctx = talloc_new(NULL);
107                 if (ret->talloc_ctx == NULL) {
108                         return NULL;
109                 }
110
111                 if (talloc_steal(ret->talloc_ctx, mem_ctx) == NULL) {
112                         return NULL;
113                 }
114                 talloc_set_name_const(ret->talloc_ctx, py_type->tp_name);
115                 ret->ptr = ptr;
116                 return (PyObject *)ret;
117         } else {
118                 PyErr_SetString(PyExc_RuntimeError,
119                                 "pytalloc_steal_ex() called for object type "
120                                 "not based on talloc");
121                 return NULL;
122         }
123 }
124
125 /**
126  * Import an existing talloc pointer into a Python object.
127  */
128 _PUBLIC_ PyObject *pytalloc_steal(PyTypeObject *py_type, void *ptr)
129 {
130         return pytalloc_steal_ex(py_type, ptr, ptr);
131 }
132
133
134 /**
135  * Import an existing talloc pointer into a Python object, leaving the
136  * original parent, and creating a reference to the object in the python
137  * object.
138  *
139  * We remember the object we hold the reference to (a
140  * possibly-non-talloc pointer), the existing parent (typically the
141  * start of the array) and the new referenced parent.  That way we can
142  * cope with the fact that we will have multiple parents, one per time
143  * python sees the object.
144  */
145 _PUBLIC_ PyObject *pytalloc_reference_ex(PyTypeObject *py_type,
146                                          TALLOC_CTX *mem_ctx, void *ptr)
147 {
148         PyTypeObject *BaseObjectType = pytalloc_GetBaseObjectType();
149         PyTypeObject *ObjectType = pytalloc_GetObjectType();
150
151         if (mem_ctx == NULL) {
152                 return PyErr_NoMemory();
153         }
154
155         if (PyType_IsSubtype(py_type, BaseObjectType)) {
156                 pytalloc_BaseObject *ret
157                         = (pytalloc_BaseObject *)py_type->tp_alloc(py_type, 0);
158                 ret->talloc_ctx = talloc_new(NULL);
159                 if (ret->talloc_ctx == NULL) {
160                         return NULL;
161                 }
162                 if (talloc_reference(ret->talloc_ctx, mem_ctx) == NULL) {
163                         return NULL;
164                 }
165                 talloc_set_name_const(ret->talloc_ctx, py_type->tp_name);
166                 ret->talloc_ptr_ctx = mem_ctx;
167                 ret->ptr = ptr;
168                 return (PyObject *)ret;
169         } else if (PyType_IsSubtype(py_type, ObjectType)) {
170                 pytalloc_Object *ret
171                         = (pytalloc_Object *)py_type->tp_alloc(py_type, 0);
172                 ret->talloc_ctx = talloc_new(NULL);
173                 if (ret->talloc_ctx == NULL) {
174                         return NULL;
175                 }
176                 if (talloc_reference(ret->talloc_ctx, mem_ctx) == NULL) {
177                         return NULL;
178                 }
179                 talloc_set_name_const(ret->talloc_ctx, py_type->tp_name);
180                 ret->ptr = ptr;
181                 return (PyObject *)ret;
182         } else {
183                 PyErr_SetString(PyExc_RuntimeError,
184                                 "pytalloc_reference_ex() called for object type "
185                                 "not based on talloc");
186                 return NULL;
187         }
188 }
189
190 #if PY_MAJOR_VERSION < 3
191
192 static void py_cobject_talloc_free(void *ptr)
193 {
194         talloc_free(ptr);
195 }
196
197 _PUBLIC_ PyObject *pytalloc_CObject_FromTallocPtr(void *ptr)
198 {
199         if (ptr == NULL) {
200                 Py_RETURN_NONE;
201         }
202         return PyCObject_FromVoidPtr(ptr, py_cobject_talloc_free);
203 }
204
205 #endif
206
207 _PUBLIC_ int pytalloc_Check(PyObject *obj)
208 {
209         PyTypeObject *tp = pytalloc_GetObjectType();
210
211         return PyObject_TypeCheck(obj, tp);
212 }
213
214 _PUBLIC_ int pytalloc_BaseObject_check(PyObject *obj)
215 {
216         PyTypeObject *tp = pytalloc_GetBaseObjectType();
217
218         return PyObject_TypeCheck(obj, tp);
219 }
220
221 _PUBLIC_ size_t pytalloc_BaseObject_size(void)
222 {
223         return sizeof(pytalloc_BaseObject);
224 }
225
226 _PUBLIC_ void *_pytalloc_get_type(PyObject *py_obj, const char *type_name)
227 {
228         void *ptr = _pytalloc_get_ptr(py_obj);
229         void *type_obj = talloc_check_name(ptr, type_name);
230
231         if (type_obj == NULL) {
232                 const char *name = talloc_get_name(ptr);
233                 PyErr_Format(PyExc_TypeError, "pytalloc: expected %s, got %s",
234                              type_name, name);
235                 return NULL;
236         }
237
238         return ptr;
239 }
240
241 _PUBLIC_ void *_pytalloc_get_ptr(PyObject *py_obj)
242 {
243         if (pytalloc_BaseObject_check(py_obj)) {
244                 return ((pytalloc_BaseObject *)py_obj)->ptr;
245         }
246         if (pytalloc_Check(py_obj)) {
247                 return ((pytalloc_Object *)py_obj)->ptr;
248         }
249         return NULL;
250 }
251
252 _PUBLIC_ TALLOC_CTX *_pytalloc_get_mem_ctx(PyObject *py_obj)
253 {
254         if (pytalloc_BaseObject_check(py_obj)) {
255                 return ((pytalloc_BaseObject *)py_obj)->talloc_ptr_ctx;
256         }
257         if (pytalloc_Check(py_obj)) {
258                 return ((pytalloc_Object *)py_obj)->talloc_ctx;
259         }
260         return NULL;
261 }
262
263 _PUBLIC_ int pytalloc_BaseObject_PyType_Ready(PyTypeObject *type)
264 {
265         PyTypeObject *talloc_type = pytalloc_GetBaseObjectType();
266         if (talloc_type == NULL) {
267                 PyErr_Format(PyExc_TypeError, "pytalloc: unable to get talloc.BaseObject type");
268                 return -1;
269         }
270
271         type->tp_base = talloc_type;
272         type->tp_basicsize = pytalloc_BaseObject_size();
273
274         return PyType_Ready(type);
275 }