Library of Assembled Shared Sources
module_definition.cpp
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2025 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43#include "python_common.h"
44#include "module_definition.h"
45#include "enum_definition.h"
46
47
48#if PY_VERSION_HEX < 0x030a0000 // < 3.10
49
50namespace
51{
52
53int PyModule_AddObjectRef(PyObject* module, const char* name, PyObject* value)
54{
55 Py_XINCREF(value);
56 if (PyModule_AddObject(module, name, value) != 0)
57 {
58 Py_XDECREF(value);
59 return -1;
60 }
61 return 0;
62}
63
64}
65
66#endif
67
68namespace lass
69{
70namespace python
71{
72
74 module_(0),
75 isInjected_(false)
76{
77 PyModuleDef def = {
78 PyModuleDef_HEAD_INIT,
79 0, /* m_name */
80 0, /* m_doc */
81 0, /* m_size */
82 0, /* m_methods */
83 0, /* m_reload */
84 0, /* m_traverse */
85 0, /* m_clear */
86 0, /* m_free */
87 };
88 def_ = def;
89
91 setDoc(doc);
92}
93
94namespace experimental
95{
96 typedef std::unique_ptr<char[]> TScopedCString;
97 void assignScopedCString(TScopedCString& out, const char* in)
98 {
99 if (in)
100 {
101 const size_t n = strlen(in) + 1;
102 TScopedCString temp(new char[n]);
103 memcpy(temp.get(), in, n);
104 out.swap(temp);
105 }
106 else
107 {
108 out.reset();
109 }
110 }
111}
112
114{
115 experimental::assignScopedCString(name_, name);
116}
117
119{
120 experimental::assignScopedCString(doc_, doc);
121}
122
123/** callback called at start of module injection
124 */
126{
127 preInject_ = callback;
128}
129
130/** callback called at end of module injection, with module as parameter
131 */
133{
134 postInject_ = callback;
135}
136
137/** new-style class addition.
138 *
139 * This function adds a class definition to a table that will be injected when the module is.
140 */
142{
143 LASS_ASSERT(!isInjected_);
144 classes_.push_back(&classDef);
145}
146
148{
149 LASS_ASSERT(!isInjected_);
150 enums_.push_back(enumDef);
151}
152
153void ModuleDefinition::addObject(PyObject* object, const char* name)
154{
155 LASS_ASSERT(!isInjected_);
156 NamedObject *tempObject = new NamedObject;
157 experimental::assignScopedCString(tempObject->name, name);
158 tempObject->object = object;
159 objects_.push_back(tempObject);
160}
161
162void ModuleDefinition::addLong(long object, const char* name)
163{
164 LASS_ASSERT(!isInjected_);
165 LongObject *tempObject = new LongObject;
166 experimental::assignScopedCString(tempObject->name, name);
167 tempObject->object = object;
168 longObjects_.push_back(tempObject);
169}
170
171void ModuleDefinition::addString(const char* object, const char* name)
172{
173 LASS_ASSERT(!isInjected_);
174 StringObject* tempObject = new StringObject;
175 experimental::assignScopedCString(tempObject->name, name);
176 experimental::assignScopedCString(tempObject->object, object);
177 stringObjects_.push_back(tempObject);
178}
179
180
182 PyCFunction dispatcher, const char* name, const char* doc, PyCFunction& overloadChain)
183{
184 TMethods::iterator i = ::std::find_if(methods_.begin(), methods_.end(), impl::NamePredicate(name));
185 if (i == methods_.end())
186 {
187 methods_.push_back(impl::createPyMethodDef(name, dispatcher, METH_VARARGS , doc));
188 overloadChain = 0;
189 }
190 else
191 {
192 overloadChain = i->ml_meth;
193 i->ml_meth = dispatcher;
194 };
195}
196
197
198void ModuleDefinition::injectLong(const char* name, long value)
199{
200 LASS_ENFORCE_POINTER(module_);
201 PyModule_AddIntConstant(module_, name, value);
202}
203
204
205void ModuleDefinition::injectString(const char* name, const char* value)
206{
207 LASS_ENFORCE_POINTER(module_);
208 PyModule_AddStringConstant(module_, name, value);
209}
210
211
212/** old-style class injection.
213 *
214 * This function can injects a class in an already-injected module
215 * @deprecated
216 */
218{
219 LASS_ENFORCE_POINTER(module_);
220 const char* shortName = classDef.name(); // finalizePyType will expand tp_name with module name.
221 PyObject* type = classDef.freezeDefinition(module_);
222 return PyModule_AddObjectRef(module_, const_cast<char*>(shortName), type) == 0;
223}
224
225
226
228{
229 PyObject* mod = doInject();
230 if (!mod)
231 {
232 chainErrFormat(PyExc_ImportError, "Failed to import '%s'", name_.get());
233 }
234 return mod;
235}
236
237
238
239PyObject* ModuleDefinition::doInject()
240{
241 if (isInjected_)
242 {
243 // this can happen when the module was imported before, and then removed
244 // from sys.modules, and then re-imported. In that case, the module will
245 // be injected again. The only thing we can do is to return the same
246 // module again.
247 Py_INCREF(module_);
248 return module_;
249 }
250 if (!module_)
251 {
252 LASS_ASSERT(name_.get());
253 preInject_();
254 methods_.push_back(impl::createPyMethodDef(0, 0, 0, 0));
255 Py_Initialize();
256 def_.m_name = name_.get();
257 def_.m_doc = doc_.get();
258 def_.m_methods = &methods_[0];
259 module_ = PyModule_Create(&def_);
260 if (!module_)
261 {
262 return nullptr;
263 }
264 }
265 for (auto def: classes_)
266 {
267 if (!injectClass(*def) != 0)
268 {
269 return nullptr;
270 }
271 }
272 for (auto def: enums_)
273 {
274 PyObject* enumType = def->freezeDefinition(name_.get(), nullptr);
275 if (PyModule_AddObjectRef(module_, def->name(), enumType) != 0)
276 {
277 return nullptr;
278 }
279 }
280 for (TObjects::const_iterator obj = objects_.begin(); obj != objects_.end(); ++obj)
281 {
282 PyModule_AddObject(module_, (*obj)->name.get(), (*obj)->object);
283 }
284 for (TLongObjects::const_iterator obj = longObjects_.begin(); obj != longObjects_.end(); ++obj)
285 {
286 if (PyModule_AddIntConstant(module_, (*obj)->name.get(), (*obj)->object) != 0)
287 {
288 return nullptr;
289 }
290 }
291 for (TStringObjects::const_iterator obj = stringObjects_.begin(); obj != stringObjects_.end(); ++obj)
292 {
293 if (PyModule_AddStringConstant(module_, (*obj)->name.get(), (*obj)->object.get()) != 0)
294 {
295 return nullptr;
296 }
297 }
298 postInject_(module_);
299 isInjected_ = true;
300 Py_INCREF(module_);
301 return module_;
302}
303
304}
305}
306
307// EOF
Base class of all enum definitions.
void injectLong(const char *name, long value)
Inject a long integer directly into an already created module.
bool injectClass(impl::ClassDefinition &classDef)
Inject a class definition directly into an already created module.
const char * doc() const
Get the module documentation string.
const char * name() const
Get the module name.
void setPreInject(const TPreInject &callback)
Set callback to be executed before module injection.
void setDoc(const char *doc)
Set the module documentation string.
void addObject(PyObject *object, const char *name)
Add an arbitrary Python object to the module.
void injectString(const char *name, const char *value)
Inject a string directly into an already created module.
void addLong(long object, const char *name)
Add a long integer constant to the module.
ModuleDefinition(const char *name, const char *doc=0)
Construct module definition with name and optional documentation.
void setName(const char *name)
Set the module name.
void addEnum(EnumDefinitionBase *enumDef)
Add an enum definition to the module.
util::Callback1< PyObject * > TPostInject
Callback type for post-injection hooks (called after module creation with module object).
void addFunctionDispatcher(PyCFunction dispatcher, const char *name, const char *doc, PyCFunction &overloadChain)
Add a function dispatcher to the module.
util::Callback0 TPreInject
Callback type for pre-injection hooks (called before module creation).
void setPostInject(const TPostInject &callback)
Set callback to be executed after module injection.
void addString(const char *object, const char *name)
Add a string constant to the module.
PyObject * inject()
Create and inject the Python module with all accumulated definitions.
void addClass(impl::ClassDefinition &classDef)
Add a class definition to the module.
Definition of a Python class.
const char * name() const
Get the class name.
PyObject * freezeDefinition(PyObject *module=nullptr)
Finalize the definition and create the Python type.
PyObject * chainErrFormat(PyObject *exception, const char *format,...)
Raise an explicitly chained Python exception.
Comprehensive C++ to Python binding library.
Library for Assembled Shared Sources.
Definition config.h:53