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