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