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 pyd.util.conv; 33 import pyd.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