1 /** 2 Wrapping functionality for D to Python. 3 */ 4 module autowrap.pynih.wrap; 5 6 7 public import std.typecons: Yes, No; 8 public import autowrap.reflection: Modules, Module, isModule; 9 public import autowrap.types: LibraryName, PreModuleInitCode, PostModuleInitCode; 10 static import python.boilerplate; 11 import python.raw: isPython2, isPython3; 12 import std.meta: allSatisfy; 13 14 15 /** 16 Returns a string to mixin that implements the necessary boilerplate 17 to create a Python library containing one Python module 18 wrapping all relevant D code and data structures. 19 */ 20 string wrapDlang( 21 LibraryName libraryName, 22 Modules modules, 23 PreModuleInitCode preModuleInitCode = PreModuleInitCode(), 24 PostModuleInitCode postModuleInitCode = PostModuleInitCode(), 25 ) 26 () 27 { 28 return __ctfe 29 ? createPythonModuleMixin!(libraryName, modules) 30 : null; 31 } 32 33 34 string createPythonModuleMixin(LibraryName libraryName, Modules modules) 35 () 36 { 37 import std.format: format; 38 import std.algorithm: map; 39 import std.array: join; 40 41 if(!__ctfe) return null; 42 43 const modulesList = modules.value.map!(a => a.toString).join(", "); 44 45 return q{ 46 import python.raw: PyDateTime_CAPI; 47 48 // This is declared as an extern C variable in python.bindings. 49 // We declare it here to avoid linker errors. 50 export __gshared extern(C) PyDateTime_CAPI* PyDateTimeAPI; 51 52 extern(C) export auto %s() { // -> ModuleInitRet 53 import autowrap.pynih.wrap: createPythonModule, LibraryName; 54 import autowrap.reflection: Module; 55 return createPythonModule!( 56 LibraryName("%s"), 57 %s 58 ); 59 } 60 }.format( 61 pyInitFuncName(libraryName), // extern(C) function name 62 libraryName.value, 63 modulesList, 64 ); 65 } 66 67 68 private string pyInitFuncName(LibraryName libraryName) @safe pure nothrow { 69 70 string prefix() { 71 static if(isPython2) 72 return "init"; 73 else static if(isPython3) 74 return "PyInit_"; 75 else 76 static assert(false); 77 } 78 79 return prefix ~ libraryName.value; 80 } 81 82 83 auto createPythonModule(LibraryName libraryName, modules...)() 84 if(allSatisfy!(isModule, modules)) 85 { 86 import autowrap.common: toSnakeCase; 87 import python.type: PythonFunction; 88 import python.boilerplate: Module, CFunctions, CFunction, Aggregates; 89 import autowrap.reflection: AllAggregates, AllFunctions; 90 import std.meta: Filter, templateNot, staticMap; 91 import std.traits: fullyQualifiedName; 92 93 alias allFunctions = AllFunctions!modules; 94 enum isWrappableFunction(alias functionSymbol) = 95 __traits(compiles, &PythonFunction!(functionSymbol.symbol)._py_function_impl); 96 alias wrappableFunctions = Filter!(isWrappableFunction, allFunctions); 97 alias nonWrappableFunctions = Filter!(templateNot!isWrappableFunction, allFunctions); 98 99 static foreach(nonWrappableFunction; nonWrappableFunctions) { 100 pragma(msg, "autowrap WARNING: Could not wrap function ", fullyQualifiedName!nonWrappableFunction); 101 } 102 103 alias toCFunction(alias functionSymbol) = CFunction!( 104 PythonFunction!(functionSymbol.symbol)._py_function_impl, 105 functionSymbol.name.toSnakeCase, 106 ); 107 alias cfunctions = CFunctions!(staticMap!(toCFunction, wrappableFunctions)); 108 109 alias allAggregates = AllAggregates!modules; 110 alias aggregates = Aggregates!allAggregates; 111 112 enum pythonModule = python.boilerplate.Module(libraryName.value); 113 114 mixin createPythonModule!(pythonModule, cfunctions, aggregates); 115 return _py_init_impl; 116 } 117 118 119 mixin template createPythonModule(python.boilerplate.Module module_, alias cfunctions, alias aggregates) 120 if(isPython3) 121 { 122 static extern(C) export auto _py_init_impl() { // -> ModuleInitRet 123 import python.raw: pyDateTimeImport; 124 import python.cooked: createModule; 125 import core.runtime: rt_init; 126 127 rt_init; 128 129 pyDateTimeImport; 130 return createModule!(module_, cfunctions, aggregates); 131 } 132 } 133 134 135 mixin template createPythonModule(python.boilerplate.Module module_, alias cfunctions, alias aggregates) 136 if(isPython2) 137 { 138 static extern(C) export void _py_init_impl() { 139 import python.raw: pyDateTimeImport; 140 import python.cooked: initModule; 141 import core.runtime: rt_init; 142 143 rt_init; 144 145 pyDateTimeImport; 146 initModule!(module_, cfunctions, aggregates); 147 } 148 }