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, Ignore; 10 static import python.boilerplate; 11 import std.meta: allSatisfy; 12 13 14 /** 15 Returns a string to mixin that implements the necessary boilerplate 16 to create a Python library containing one Python module 17 wrapping all relevant D code and data structures. 18 */ 19 string wrapDlang( 20 LibraryName libraryName, 21 Modules modules, 22 RootNamespace _ = RootNamespace(), // ignored in this backend 23 PreModuleInitCode preModuleInitCode = PreModuleInitCode(), // ignored in this backend 24 PostModuleInitCode postModuleInitCode = PostModuleInitCode(), // ignored in this backend 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.types: 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 import python.raw: isPython3; 70 static assert(isPython3, "Python2 no longer supported"); 71 return "PyInit_" ~ libraryName.value; 72 } 73 74 75 auto createPythonModule(LibraryName libraryName, modules...)() 76 if(allSatisfy!(isModule, modules)) 77 { 78 import autowrap.common: toSnakeCase, AlwaysTry; 79 import python.type: PythonFunction; 80 import python.boilerplate: Module, CFunctions, CFunction, Aggregates; 81 import python.raw: PyModule_AddIntConstant, PyModule_AddStringConstant; 82 import autowrap.reflection: AllAggregates, AllFunctions, AllConstants; 83 import std.meta: Filter, templateNot, staticMap; 84 import std.traits: fullyQualifiedName; 85 86 static immutable char[1] emptyString = ['\0']; 87 88 alias allFunctions = AllFunctions!modules; 89 enum isWrappableFunction(alias functionSymbol) = AlwaysTry || 90 __traits(compiles, &PythonFunction!(functionSymbol.symbol)._py_function_impl); 91 alias wrappableFunctions = Filter!(isWrappableFunction, allFunctions); 92 alias nonWrappableFunctions = Filter!(templateNot!isWrappableFunction, allFunctions); 93 94 static foreach(nonWrappableFunction; nonWrappableFunctions) {{ 95 pragma(msg, "autowrap WARNING: Could not wrap function ", 96 fullyQualifiedName!(nonWrappableFunction.symbol)); 97 // uncomment to see the compiler error 98 // auto ptr = &PythonFunction!(nonWrappableFunction.symbol)._py_function_impl; 99 }} 100 101 alias toCFunction(alias functionSymbol) = CFunction!( 102 PythonFunction!(functionSymbol.symbol)._py_function_impl, 103 functionSymbol.identifier.toSnakeCase, 104 ); 105 alias cfunctions = CFunctions!(staticMap!(toCFunction, wrappableFunctions)); 106 107 alias allAggregates = AllAggregates!modules; 108 alias aggregates = Aggregates!allAggregates; 109 110 enum pythonModule = python.boilerplate.Module(libraryName.value); 111 112 mixin createPythonModule!(pythonModule, cfunctions, aggregates); 113 auto ret = _py_init_impl(); 114 115 template isIntegral(alias var) { 116 import std.traits: _isIntegral = isIntegral; 117 enum isIntegral = _isIntegral!(var.Type); 118 } 119 120 enum isString(alias var) = is(var.Type == string); 121 122 alias constants = AllConstants!modules; 123 static foreach(intConstant; Filter!(isIntegral, constants)) { 124 PyModule_AddIntConstant(ret, &intConstant.identifier[0], intConstant.value); 125 } 126 static foreach(strConstant; Filter!(isString, constants)) { 127 128 // We can't pass a null pointer if the value of the string constant is empty 129 PyModule_AddStringConstant(ret, 130 strConstant.identifier.ptr, 131 strConstant.value.length ? strConstant.value.ptr : &emptyString[0]); 132 } 133 134 return ret; 135 } 136 137 138 mixin template createPythonModule(python.boilerplate.Module module_, alias cfunctions, alias aggregates) 139 { 140 static extern(C) export auto _py_init_impl() { // -> ModuleInitRet 141 import python.raw: pyDateTimeImport; 142 import python.cooked: createModule; 143 import core.runtime: rt_init; 144 145 rt_init; 146 147 pyDateTimeImport; 148 return createModule!(module_, cfunctions, aggregates); 149 } 150 }