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