1 /*
2 Copyright (c) 2006 Kirk McDonald
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22 
23 /**
24   Contains utilities for wrapping D functions.
25   */
26 module pyd.def;
27 
28 import deimos.python.Python;
29 
30 import std.typetuple;
31 import std.traits;
32 import util.conv;
33 import util.typelist;
34 import pyd.func_wrap;
35 
36 private PyMethodDef[] module_global_methods = [
37     { null, null, 0, null }
38 ];
39 
40 private PyMethodDef[][string] module_methods;
41 version(Python_3_0_Or_Later) {
42     private PyModuleDef*[string] pyd_moduledefs;
43 }
44 PyObject*[string] pyd_modules;
45 
46 // this appears to be a python3-only thing that holds instantiators
47 // for wrapped classes and structs
48 private void delegate()[string][string] pyd_module_classes;
49 
50 private void ready_module_methods(string modulename) {
51     PyMethodDef empty;
52     if (!(modulename in module_methods)) {
53         module_methods[modulename] = (PyMethodDef[]).init;
54         module_methods[modulename] ~= empty;
55     }
56 }
57 
58 
59 PyObject* Pyd_Module_p(string modulename="") {
60     PyObject** m = modulename in pyd_modules;
61     if (m is null) return null;
62     else return *m;
63 }
64 
65 bool should_defer_class_wrap(string modulename, string classname) {
66     version(Python_3_0_Or_Later) {
67     return !(modulename in pyd_modules) && (modulename in pyd_module_classes)
68         && !(classname in pyd_module_classes[modulename]);
69     }else {
70         return false;
71     }
72 }
73 void defer_class_wrap(string modulename, string classname,
74         void delegate() wrapper) {
75     pyd_module_classes[modulename][classname] = wrapper;
76 }
77 
78 /// Param of def
79 struct ModuleName(string _modulename) {
80     enum modulename = _modulename;
81 }
82 template IsModuleName(T...) {
83     import std.algorithm: startsWith;
84     enum bool IsModuleName = T[0].stringof.startsWith("ModuleName!");
85 }
86 
87 /// Param of def, Def, StaticDef
88 struct Docstring(string _doc) {
89     enum doc = _doc;
90 }
91 
92 template IsDocstring(T...) {
93     import std.algorithm: startsWith;
94     enum bool IsDocstring = T[0].stringof.startsWith("Docstring!");
95 }
96 /// Param of def, Def, StaticDef
97 struct PyName(string _name) {
98     enum name = _name;
99 }
100 template IsPyName(T...) {
101     import std.algorithm: startsWith;
102     enum bool IsPyName = T[0].stringof.startsWith("PyName!");
103 }
104 
105 /// Param of Property, Member
106 struct Mode(string _mode) {
107     enum mode = _mode;
108 }
109 template IsMode(T...) {
110     import std.algorithm: startsWith;
111     enum bool IsMode = T[0].stringof.startsWith("Mode!");
112 }
113 
114 struct Args(string default_modulename,
115             string default_docstring,
116             string default_pyname,
117             string default_mode,
118             Params...) {
119     alias Filter!(IsDocstring, Params) Docstrings;
120     static if(Docstrings.length) {
121         enum docstring = Docstrings[0].doc;
122     }else{
123         enum docstring = default_docstring;
124     }
125     alias Filter!(IsPyName, Params) PyNames;
126     static if(PyNames.length) {
127         enum pyname = PyNames[0].name;
128     }else{
129         enum pyname = default_pyname;
130     }
131     alias Filter!(IsMode, Params) Modes;
132     static if(Modes.length) {
133         enum mode = Modes[0].mode;
134     }else{
135         enum mode = default_mode;
136     }
137     alias Filter!(IsModuleName, Params) ModuleNames;
138     static if(ModuleNames.length) {
139         enum modulename = ModuleNames[0].modulename;
140     }else{
141         enum modulename = default_modulename;
142     }
143 
144     alias Filter!(templateNot!IsModuleName,
145           Filter!(templateNot!IsDocstring,
146           Filter!(templateNot!IsPyName,
147           Filter!(templateNot!IsMode,
148               Params)))) rem;
149     template IsString(T...) {
150         enum bool IsString = is(typeof(T[0]) == string);
151     }
152     static if(Filter!(IsString, rem).length) {
153         static assert(false, "string parameters must be wrapped with Docstring, Mode, etc");
154     }
155 }
156 
157 /**
158 Wraps a D function, making it callable from Python.
159 
160 Supports default arguments, typesafe variadic arguments, and python's
161 keyword arguments.
162 
163 Params:
164 
165 fn   = The function to wrap.
166 Options = Optional parameters. Takes Docstring!(docstring), PyName!(pyname), ModuleName!(modulename), and fn_t
167 modulename = The name of the python module in which the wrapped function
168             resides.
169 pyname = The name of the function as it will appear in Python.
170 fn_t = The function type of the function to wrap. This must be
171             specified if more than one function shares the same name,
172             otherwise the first one defined lexically will be used.
173 docstring = The function's docstring.
174 
175 Examples:
176 ---
177 import pyd.pyd;
178 string foo(int i) {
179     if (i > 10) {
180         return "It's greater than 10!";
181     } else {
182         return "It's less than 10!";
183     }
184 }
185 extern (C)
186 export void inittestdll() {
187     def!(foo, ModuleName!"testdll");
188     add_module("testdll");
189 }
190 ---
191  And in Python:
192 $(D_CODE >>> import testdll
193 >>> print testdll.foo(20)
194 It's greater than 10!)
195  */
196 
197 
198 void def(alias _fn, Options...)() {
199     alias Args!("","", __traits(identifier,_fn), "",Options) args;
200     static if(args.rem.length) {
201         alias args.rem[0] fn_t;
202     }else {
203         alias typeof(&_fn) fn_t;
204     }
205     alias def_selector!(_fn, fn_t).FN fn;
206 
207     PyMethodDef empty;
208     ready_module_methods(args.modulename);
209     PyMethodDef[]* list = &module_methods[args.modulename];
210 
211     (*list)[$-1].ml_name = (args.pyname ~ "\0").dup.ptr;
212     (*list)[$-1].ml_meth = cast(PyCFunction) &function_wrap!(fn,args.pyname).func;
213     (*list)[$-1].ml_flags = METH_VARARGS | METH_KEYWORDS;
214     (*list)[$-1].ml_doc = (args.docstring ~ "\0").dup.ptr;
215     (*list) ~= empty;
216 }
217 
218 template Typeof(alias fn0) {
219     alias typeof(&fn0) Typeof;
220 }
221 
222 template def_selector(alias fn, fn_t) {
223     import std.string: format;
224 
225     alias alias_selector!(fn, fn_t) als;
226     static if(als.VOverloads.length == 0 && als.Overloads.length != 0) {
227         alias staticMap!(Typeof, als.Overloads) OverloadsT;
228         static assert(0, format("%s not among %s",
229                     fn_t.stringof,OverloadsT.stringof));
230     }else static if(als.VOverloads.length > 1){
231         static assert(0, format("%s: Cannot choose between %s", als.nom,
232                     staticMap!(Typeof, als.VOverloads)));
233     }else{
234         alias als.VOverloads[0] FN;
235     }
236 }
237 
238 template IsEponymousTemplateFunction(alias fn) {
239     // dmd issue 13372: its not a bug, its a feature!
240     alias TypeTuple!(__traits(parent, fn))[0] Parent;
241     enum IsEponymousTemplateFunction = is(typeof(Parent) == typeof(fn));
242 }
243 
244 template alias_selector(alias fn, fn_t) {
245     alias ParameterTypeTuple!fn_t ps;
246     alias ReturnType!fn_t ret;
247     alias TypeTuple!(__traits(parent, fn))[0] Parent;
248     enum nom = __traits(identifier, fn);
249     template IsDesired(alias f) {
250         alias ParameterTypeTuple!f fps;
251         alias ReturnType!f fret;
252         enum bool IsDesired = is(ps == fps) && is(fret == ret);
253     }
254     static if(IsEponymousTemplateFunction!fn) {
255         alias TypeTuple!(fn) Overloads;
256     }else{
257         alias TypeTuple!(__traits(getOverloads, Parent, nom)) Overloads;
258     }
259     alias Filter!(IsDesired, Overloads) VOverloads;
260 }
261 
262 string pyd_module_name;
263 
264 /// true after Py_Finalize has been called.
265 /// Playing with the Python API when this is true
266 /// is not advised.
267 __gshared Py_Finalize_called = false;
268 
269 extern(C) void Py_Finalize_hook() {
270     import thread = pyd.thread;
271     Py_Finalize_called = true;
272     thread.detachAll();
273 }
274 
275 version(PydPythonExtension)
276 {
277     /// For embedding python
278     void py_init()()
279     {
280         static assert(false, "py_init should only be called when embedding python");
281     }
282 }
283 else
284 {
285     /// For embedding python
286     void py_init() {
287         doActions(PyInitOrdering.Before);
288         Py_Initialize();
289         py_init_called = true;
290         doActions(PyInitOrdering.After);
291         version(Python_3_0_Or_Later) {
292             // stinking python 3 lazy initializes modules.
293             import pyd.embedded;
294             foreach(modulename, _; pyd_module_classes) {
295                 py_import(modulename);
296             }
297         }
298     }
299 }
300 
301 /// For embedding python, should you wish to restart the interpreter.
302 void py_finish() {
303     Py_Finalize();
304     Py_Finalize_hook();
305     py_init_called = false;
306 }
307 
308 /**
309  * Module initialization function. Should be called after the last call to def.
310  * For extending python.
311  */
312 PyObject* module_init(string docstring="") {
313     Py_AtExit(&Py_Finalize_hook);
314     string name = pyd_module_name;
315     ready_module_methods("");
316     version(Python_3_0_Or_Later) {
317         PyModuleDef* modl = pyd_moduledefs[""] = new PyModuleDef;
318         (cast(PyObject*) modl).ob_refcnt = 1;
319         modl.m_name = zcc(name);
320         modl.m_doc = zcc(docstring);
321         modl.m_size = -1;
322         modl.m_methods = module_methods[""].ptr;
323         pyd_module_classes[""] = (void delegate()[string]).init;
324 
325         Py3_ModuleInit!"".func();
326     }else {
327         pyd_modules[""] = Py_INCREF(Py_InitModule3((name ~ "\0"),
328                     module_methods[""].ptr, (docstring ~ "\0")));
329     }
330     doActions(PyInitOrdering.Before);
331     doActions(PyInitOrdering.After);
332     py_init_called = true;
333     return pyd_modules[""];
334 }
335 
336 /**
337 Module initialization function. Should be called after the last call to def.
338 
339 This may not be supportable in Python 3 extensions.
340 
341 Params
342 Options = Optional parameters. Takes Docstring!(docstring), and ModuleName!(modulename)
343 modulename = name of module
344 docstring = docstring of module
345  */
346 void add_module(Options...)() {
347     alias Args!("","", "", "",Options) args;
348     enum modulename = args.modulename;
349     enum docstring = args.docstring;
350     ready_module_methods(modulename);
351     version(Python_3_0_Or_Later) {
352         assert(!py_init_called);
353         version(PydPythonExtension) {
354             static assert(false, "add_module is not properly supported in python3 at this time");
355         }
356         PyModuleDef* modl = new PyModuleDef;
357         Py_SET_REFCNT(modl, 1);
358         modl.m_name = zcc(modulename);
359         modl.m_doc = zcc(docstring);
360         modl.m_size = -1;
361         modl.m_methods = module_methods[modulename].ptr;
362         pyd_moduledefs[modulename] = modl;
363         pyd_module_classes[modulename] = (void delegate()[string]).init;
364 
365         PyImport_AppendInittab(modulename.ptr, &Py3_ModuleInit!modulename.func);
366     }else{
367         // schizophrenic arrangements, these
368         version(PydPythonExtension) {
369         }else{
370             assert(py_init_called);
371         }
372         pyd_modules[modulename] = Py_INCREF(Py_InitModule3((modulename ~ "\0"),
373                     module_methods[modulename].ptr, (docstring ~ "\0")));
374     }
375 }
376 
377 template Py3_ModuleInit(string modulename) {
378     extern(C) PyObject* func() {
379         pyd_modules[modulename] = PyModule_Create(pyd_moduledefs[modulename]);
380         foreach(n,action; pyd_module_classes[modulename]) {
381             action();
382         }
383         return pyd_modules[modulename];
384     }
385 }
386 
387 bool py_init_called = false;
388 void delegate()[] before_py_init_deferred_actions;
389 void delegate()[] after_py_init_deferred_actions;
390 
391 void doActions(PyInitOrdering which) {
392     auto actions = before_py_init_deferred_actions;
393     if (which == PyInitOrdering.Before) {
394     }else if(which == PyInitOrdering.After) {
395         actions = after_py_init_deferred_actions;
396     }else assert(false);
397     foreach(action; actions) {
398         action();
399     }
400 }
401 
402 ///
403 enum PyInitOrdering{
404     /// call will be made before Py_Initialize.
405     Before,
406     /// call will be made after Py_Initialize.
407     After,
408 }
409 
410 /// call will be made at the appropriate time for initializing
411 /// modules.  (for python 2, it should be after Py_Initialize,
412 /// for python 3, before).
413 version(Python_3_0_Or_Later) {
414     enum PyInitOrdering ModuleInit = PyInitOrdering.Before;
415 }else{
416     enum PyInitOrdering ModuleInit = PyInitOrdering.After;
417 }
418 
419 /**
420   Use this to wrap calls to add_module and the like.
421 
422   py_init will ensure they are called at the appropriate time
423   */
424 void on_py_init(void delegate() dg,
425         PyInitOrdering ord = ModuleInit) {
426     import std.exception: enforce;
427 
428     with(PyInitOrdering) switch(ord) {
429         case Before:
430             if(py_init_called) {
431                 enforce(0, "py_init has already been called");
432             }
433             before_py_init_deferred_actions ~= dg;
434             break;
435         case After:
436             if(py_init_called) {
437                 dg();
438             }
439             after_py_init_deferred_actions ~= dg;
440             break;
441         default:
442             assert(0);
443     }
444 }
445