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 }