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