Library of Assembled Shared Sources
 
Loading...
Searching...
No Matches
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-2026 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
48namespace lass
49{
50namespace python
51{
52
54 module_(0),
55 isInjected_(false)
56{
57 PyModuleDef def = {
58 PyModuleDef_HEAD_INIT,
59 0, /* m_name */
60 0, /* m_doc */
61 0, /* m_size */
62 0, /* m_methods */
63 0, /* m_reload */
64 0, /* m_traverse */
65 0, /* m_clear */
66 0, /* m_free */
67 };
68 def_ = def;
69
71 setDoc(doc);
72}
73
74namespace experimental
75{
76 typedef std::unique_ptr<char[]> TScopedCString;
77 void assignScopedCString(TScopedCString& out, const char* in)
78 {
79 if (in)
80 {
81 const size_t n = strlen(in) + 1;
82 TScopedCString temp(new char[n]);
83 memcpy(temp.get(), in, n);
84 out.swap(temp);
85 }
86 else
87 {
88 out.reset();
89 }
90 }
91}
92
94{
95 experimental::assignScopedCString(name_, name);
96}
97
99{
100 experimental::assignScopedCString(doc_, doc);
101}
102
103/** callback called at start of module injection
104 */
106{
107 preInject_ = callback;
108}
109
110/** callback called at end of module injection, with module as parameter
111 */
113{
114 postInject_ = callback;
115}
116
117/** new-style class addition.
118 *
119 * This function adds a class definition to a table that will be injected when the module is.
120 */
122{
123 LASS_ASSERT(!isInjected_);
124 classes_.push_back(&classDef);
125}
126
128{
129 LASS_ASSERT(!isInjected_);
130 enums_.push_back(enumDef);
131}
132
133void ModuleDefinition::addObject(PyObject* object, const char* name)
134{
135 LASS_ASSERT(!isInjected_);
136 NamedObject *tempObject = new NamedObject;
137 experimental::assignScopedCString(tempObject->name, name);
138 tempObject->object = object;
139 objects_.push_back(tempObject);
140}
141
142void ModuleDefinition::addLong(long object, const char* name)
143{
144 LASS_ASSERT(!isInjected_);
145 LongObject *tempObject = new LongObject;
146 experimental::assignScopedCString(tempObject->name, name);
147 tempObject->object = object;
148 longObjects_.push_back(tempObject);
149}
150
151void ModuleDefinition::addString(const char* object, const char* name)
152{
153 LASS_ASSERT(!isInjected_);
154 StringObject* tempObject = new StringObject;
155 experimental::assignScopedCString(tempObject->name, name);
156 experimental::assignScopedCString(tempObject->object, object);
157 stringObjects_.push_back(tempObject);
158}
159
160
162 PyCFunction dispatcher, const char* name, const char* doc, PyCFunction& overloadChain)
163{
164 TMethods::iterator i = ::std::find_if(methods_.begin(), methods_.end(), impl::NamePredicate(name));
165 if (i == methods_.end())
166 {
167 methods_.push_back(impl::createPyMethodDef(name, dispatcher, METH_VARARGS , doc));
168 overloadChain = 0;
169 }
170 else
171 {
172 overloadChain = i->ml_meth;
173 i->ml_meth = dispatcher;
174 };
175}
176
177
178void ModuleDefinition::injectLong(const char* name, long value)
179{
180 LASS_ENFORCE_POINTER(module_);
181 PyModule_AddIntConstant(module_, name, value);
182}
183
184
185void ModuleDefinition::injectString(const char* name, const char* value)
186{
187 LASS_ENFORCE_POINTER(module_);
188 PyModule_AddStringConstant(module_, name, value);
189}
190
191
192/** old-style class injection.
193 *
194 * This function can injects a class in an already-injected module
195 * @deprecated
196 */
198{
199 LASS_ENFORCE_POINTER(module_);
200 const char* shortName = classDef.name(); // finalizePyType will expand tp_name with module name.
201 PyObject* type = classDef.freezeDefinition(module_);
202 return PyModule_AddObjectRef(module_, const_cast<char*>(shortName), type) == 0;
203}
204
205
206
208{
209 PyObject* mod = doInject();
210 if (!mod)
211 {
212 chainErrFormat(PyExc_ImportError, "Failed to import '%s'", name_.get());
213 }
214 return mod;
215}
216
217
218
219PyObject* ModuleDefinition::doInject()
220{
221 if (isInjected_)
222 {
223 // this can happen when the module was imported before, and then removed
224 // from sys.modules, and then re-imported. In that case, the module will
225 // be injected again. The only thing we can do is to return the same
226 // module again.
227 Py_INCREF(module_);
228 return module_;
229 }
230 if (!module_)
231 {
232 LASS_ASSERT(name_.get());
233 preInject_();
234 methods_.push_back(impl::createPyMethodDef(0, 0, 0, 0));
235 Py_Initialize();
236 def_.m_name = name_.get();
237 def_.m_doc = doc_.get();
238 def_.m_methods = &methods_[0];
239 module_ = PyModule_Create(&def_);
240 if (!module_)
241 {
242 return nullptr;
243 }
244 }
245 for (auto def: classes_)
246 {
247 if (!injectClass(*def))
248 {
249 return nullptr;
250 }
251 }
252 for (auto def: enums_)
253 {
254 PyObject* enumType = def->freezeDefinition(name_.get(), nullptr);
255 if (PyModule_AddObjectRef(module_, def->name(), enumType) != 0)
256 {
257 return nullptr;
258 }
259 }
260 for (TObjects::const_iterator obj = objects_.begin(); obj != objects_.end(); ++obj)
261 {
262 PyModule_AddObject(module_, (*obj)->name.get(), (*obj)->object);
263 }
264 for (TLongObjects::const_iterator obj = longObjects_.begin(); obj != longObjects_.end(); ++obj)
265 {
266 if (PyModule_AddIntConstant(module_, (*obj)->name.get(), (*obj)->object) != 0)
267 {
268 return nullptr;
269 }
270 }
271 for (TStringObjects::const_iterator obj = stringObjects_.begin(); obj != stringObjects_.end(); ++obj)
272 {
273 if (PyModule_AddStringConstant(module_, (*obj)->name.get(), (*obj)->object.get()) != 0)
274 {
275 return nullptr;
276 }
277 }
278 postInject_(module_);
279 isInjected_ = true;
280 Py_INCREF(module_);
281 return module_;
282}
283
284}
285}
286
287// 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