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