1 module mir.pybind;
2 /** CODING GUIDE
3 
4 D or Python:
5 
6 When we can implement something both in D or Python, we do everything in D.
7 D has less overhead than Python (the reason why we use D from Python).
8 
9 Error Handling:
10 
11 D function should raise exception for python by PyErr_SetString(PyObject* type, const char* message)
12 The type can be PyExc_RuntimeError, PyExc_TypeError, etc
13 see also https://docs.python.org/3/c-api/exceptions.html#standard-exceptions
14  */
15 
16 private import deimos.python.Python : PyMethodDef, METH_VARARGS;
17 private import mir.pybind.conv : toPyFunction;
18 
19 /// template to define python function (wrapper of PyMethodDef)
20 /// TODO insert type signature and args in docstring automatically
21 enum def(alias dfunc, string doc = "", string name = __traits(identifier, dfunc))
22     = PyMethodDef(name, &toPyFunction!dfunc, METH_VARARGS, doc);
23 
24 /// template to define python module (wrapper of PyModuleDef)
25 mixin template defModule(string modName, string modDoc, PyMethodDef[] defs)
26 {
27     private import deimos.python.Python : PyModuleDef, PyModuleDef_Base, PyMethodDef;
28     private import std..string : replace;
29 
30     extern (C):
31     enum PyModuleDef_Base PyModuleDef_HEAD_INIT = {{1, null}, null, 0, null};
32 
33     enum PyMethodDef PyMethodDef_SENTINEL = {null, null, 0, null};
34 
35     void rtAtExit()
36     {
37         import core.runtime : rt_term;
38         rt_term();
39     }
40 
41     extern(D) static PyModuleDef mod = {PyModuleDef_HEAD_INIT, m_name: modName, m_doc: modDoc, m_size: -1};
42     extern(D) static methods = defs ~ [PyMethodDef_SENTINEL];
43 
44     mixin(
45         q{
46             pragma(mangle, __traits(identifier, PyInit_$))
47             auto PyInit_$()
48             {
49                 import deimos.python.Python : Py_AtExit, PyModule_Create;
50                 import core.runtime : rt_init;
51                 rt_init();
52                 Py_AtExit(&rtAtExit);
53                 mod.m_methods = methods.ptr;
54                 // TODO import_array(); // enable numpy API
55                 return PyModule_Create(&mod);
56             }
57         }.replace("$", modName));
58 }