Commits

ariovistus committed 31b33f1

implement extending embedded python 3.
it is slightly different than python2, but we seem to have both working.
tp_compare goes away in python 3, so we now use tp_richcompare for both 2 and 3
sq_slice, sq_ass_slice go away in python 3, which favors the extended slicing
api only. python 2 supports it too, so we'll implement it here in a jiffy.
pyd.embedded no longer calls py_init, because now where to call add_module and
friends is now python-version-dependent. Use of on_py_init is necessary.

pyind3 is working at least!

  • Participants
  • Parent commits f0cb709

Comments (0)

Files changed (23)

File examples/pyd_unittests/buffer.d

 import std.stdio;
 
 void main() {
-    add_module("testing");
+    on_py_init({
+            add_module!(ModuleName!"testing")();
+    });
+    py_init();
     py_stmts(
             "from numpy import eye\n"
             "a = eye(4,k=1)\n",

File examples/pyd_unittests/class_wrap.d

 import deimos.python.pyport: Py_ssize_t;
 import std.stdio;
 
-static this() {
-    add_module("testing");
+shared static this() {
+    on_py_init({
+    add_module!(ModuleName!"testing")();
+    });
+    py_init();
+    on_py_init({
     wrap_class!(Bizzy,
             ModuleName!"testing",
             //Init!(int[]),
             Repr!(Bizzy4.repr),
             Len!(),
     )();
+    }, PyInitOrdering.After);
 
 }
 

File examples/pyd_unittests/class_wrap3.d

+import pyd.pyd, pyd.embedded;
+import deimos.python.pyport: Py_ssize_t;
+import std.stdio;
+
+shared static this() {
+    on_py_init({
+    add_module!(ModuleName!"testing")();
+    });
+    py_init();
+    on_py_init({
+    wrap_class!(Bizzy,
+            ModuleName!"testing",
+            //Init!(int[]),
+            Init!(int,double,string),
+            Def!(Bizzy.a, int function(double)), 
+            StaticDef!(Bizzy.b, int function(double)),
+            Repr!(Bizzy.repr),
+            Property!(Bizzy.m, Mode!"r"),
+            OpBinary!("+"),
+            OpBinary!("*"),
+            OpBinary!("^^"),
+            OpBinaryRight!("in"),
+            OpBinaryRight!("+"),
+            OpUnary!("+"),
+            OpUnary!("~"),
+            OpAssign!("+"),
+            OpAssign!("%"),
+            OpAssign!("^^"),
+            OpIndex!(),
+            OpIndexAssign!(),
+            OpCompare!(),
+            OpSlice!(),
+            OpSliceAssign!(),
+            OpCall!(double),
+            Len!(Bizzy.pylen),
+    )();
+    wrap_class!(Bizzy2,
+            ModuleName!"testing",
+            Init!(int[]),
+            StaticDef!(Bizzy2.a),
+            StaticDef!(Bizzy2.b),
+            StaticDef!(Bizzy2.c),
+    )();
+    wrap_class!(Bizzy3,
+            ModuleName!"testing",
+            Init!(int,int),
+            Def!(Bizzy3.a),
+            Def!(Bizzy3.b),
+            Def!(Bizzy3.c),
+    )();
+    wrap_class!(Bizzy4,
+            ModuleName!"testing",
+            Property!(Bizzy4.i),
+            Repr!(Bizzy4.repr),
+            Len!(),
+    )();
+    }, PyInitOrdering.After);
+
+}
+
+class Bizzy {
+    int _m;
+
+    int m() { return _m; }
+
+    this(int i, double d = 1.0, string s = "hi") {
+        writefln("shawarma i=%s, d=%s, s='%s'",i,d,s);
+    }
+
+    int a(int i){
+        return i + 11;
+    }
+    int a(double d) {
+        return cast(int)( d+12);
+    }
+    static int b(int i){
+        return i + 13;
+    }
+    static int b(double d) {
+        return cast(int)( d+14);
+    }
+
+    string repr(int i) {
+        return "hi";
+    }
+    string repr() {
+        return "bye";
+    }
+    int opBinary(string op)(int i) {
+        static if(op == "+") return i+1;
+        else static if(op == "*") return i+2;
+        else static if(op == "^^") return i+3;
+        else static assert(0);
+    }
+    bool opBinaryRight(string op)(int i) if(op == "in") {
+        return i > 10;
+    }
+
+    int opBinaryRight(string op)(int i) if(op == "+") {
+        return i + 4;
+    }
+
+    void opOpAssign(string op)(int i) {
+        static if(op == "+") _m = i + 22;
+        else static if(op == "%") _m = i + 33;
+        else static if(op == "^^") _m = i + 44;
+        else static assert(0);
+    }
+
+    int opUnary(string op)() {
+        static if(op == "+") return 55;
+        else static if(op == "~") return 44;
+        else static assert(0);
+    }
+
+    override int opCmp(Object p) {
+        return 10;
+    }
+
+    double opIndex(int i) {
+        return i*4.4;
+    }
+
+    int[] opSlice(Py_ssize_t a, Py_ssize_t b) {
+        return [1,2,3];
+    }
+
+    void opIndexAssign(double d, int i) {
+        _m = cast(int)(d*1000) + i;
+    }
+
+    void opSliceAssign(double d, Py_ssize_t a, Py_ssize_t b) {
+        _m = cast(int)(d*1000) + cast(int)(a*10 + b);
+    }
+
+    Py_ssize_t pylen(){
+        return 401;
+    }
+
+    int opCall(double d) {
+        return _m + cast(int)(1000 *d);
+    }
+}
+
+class Bizzy2 {
+    this(int[] i...) {
+        writeln("abooba",i);
+    }
+
+    static int a(int i, double d) {
+        return cast(int)(200*d + i);
+    }
+    static int b(int i, double d = 3.2) {
+        return cast(int)(1000*d + 10*i+3);
+    }
+    static int c(int[] i...) {
+        int ret = 0;
+        foreach(_i,k; i) {
+            ret += 10 ^^ _i * k;
+        }
+        return ret;
+    }
+}
+
+class Bizzy3{
+    this(int i, int j) {
+        writefln("broomba(%s,%s)",i,j);
+    }
+
+    int a(int i, double d) {
+        return cast(int)(100*d + 2*i);
+    }
+    int b(int i, double d = 3.2) {
+        return cast(int)(1000*d + 20*i+4);
+    }
+    int c(int[] i...) {
+        int ret = 0;
+        foreach_reverse(_i,k; i) {
+            ret += 10 ^^ (i.length-_i-1) * k;
+        }
+        return ret;
+    }
+}
+
+class Bizzy4 {
+    int _i = 4;
+
+    @property int i() { return _i; }
+    @property void i(int n) { _i = n; }
+    @property size_t length() { return 5; }
+    @property string repr() { return "cowabunga"; }
+}
+
+unittest {
+    py_stmts(q"{
+#bizzy=Bizzy(1,2,3,4,5)
+#bizzy=Bizzy(d=7.1,i=4)
+bizzy=Bizzy(i=4)
+assert bizzy.a(1.0) == 13
+assert Bizzy.b(1.0) == 15
+assert repr(bizzy) == "bye"
+assert bizzy+1 == 2
+assert bizzy*1 == 3
+assert bizzy**1 == 4
+assert 1+bizzy == 5
+assert 19 in bizzy
+assert 0 not in bizzy 
+assert +bizzy == 55
+assert ~bizzy == 44
+assert bizzy > 1
+assert len(bizzy) == 401
+assert bizzy[1:2] == [1,2,3]
+bizzy += 2
+assert bizzy.m == 24
+bizzy %= 3
+assert bizzy.m == 36
+bizzy **= 4
+assert bizzy.m == 48
+bizzy[2] = 3.3
+assert bizzy.m == 3302
+bizzy[2:3] = 4.3
+assert bizzy.m == 4323
+assert bizzy(40.5) == 44823
+}", "testing");
+
+py_stmts(q"{
+bizzy = Bizzy2(4);
+bizzy = Bizzy2([4,5]);
+bizzy = Bizzy2(i=4);
+bizzy = Bizzy2(i=[4,5]);
+}", "testing");
+
+assert(py_eval!int("Bizzy2.a(7, 32.1)","testing") == 6427);
+assert(py_eval!int("Bizzy2.a(i=7, d=32.1)","testing") == 6427);
+assert(py_eval!int("Bizzy2.a(d=32.1,i=7)","testing") == 6427);
+assert(py_eval!int("Bizzy2.b(7, 32.1)","testing") == 32173);
+assert(py_eval!int("Bizzy2.b(d=32.1,i=7)","testing") == 32173);
+assert(py_eval!int("Bizzy2.b(i=7, d=32.1)","testing") == 32173);
+assert(py_eval!int("Bizzy2.b(7)","testing") == 3273);
+assert(py_eval!int("Bizzy2.b(i=7)","testing") == 3273);
+assert(py_eval!int("Bizzy2.c(7)","testing") == 7);
+assert(py_eval!int("Bizzy2.c(i=7)","testing") == 7);
+assert(py_eval!int("Bizzy2.c(i=[7])","testing") == 7);
+assert(py_eval!int("Bizzy2.c(7,5,6)","testing") == 657);
+assert(py_eval!int("Bizzy2.c(i=[7,5,6])","testing") == 657);
+
+py_stmts(q"{
+bizzy = Bizzy3(1,2)
+}", "testing");
+assert(py_eval!int("bizzy.a(7, 32.1)","testing") == 3224);
+assert(py_eval!int("bizzy.a(i=7, d=32.1)","testing") == 3224);
+assert(py_eval!int("bizzy.a(d=32.1,i=7)","testing") == 3224);
+assert(py_eval!int("bizzy.b(7, 32.1)","testing") == 32244);
+assert(py_eval!int("bizzy.b(d=32.1,i=7)","testing") == 32244);
+assert(py_eval!int("bizzy.b(i=7, d=32.1)","testing") == 32244);
+assert(py_eval!int("bizzy.b(7)","testing") == 3344);
+assert(py_eval!int("bizzy.b(i=7)","testing") == 3344);
+assert(py_eval!int("bizzy.c(7)","testing") == 7);
+assert(py_eval!int("bizzy.c(i=7)","testing") == 7);
+assert(py_eval!int("bizzy.c(i=[7])","testing") == 7);
+assert(py_eval!int("bizzy.c(7,5,6)","testing") == 756);
+assert(py_eval!int("bizzy.c(i=[7,5,6])","testing") == 756);
+
+py_stmts(q"{
+bizzy = Bizzy4()
+}", "testing");
+assert(py_eval!int("bizzy.i","testing") == 4);
+py_stmts(q"{
+bizzy.i = 10
+}", "testing");
+assert(py_eval!int("bizzy.i","testing") == 10);
+assert(py_eval!int("len(bizzy)","testing") == 5);
+assert(py_eval!string("repr(bizzy)","testing") == "cowabunga");
+
+}
+
+void main() {}

File examples/pyd_unittests/def.d

     def!(a,int function(double), ModuleName!"testing")(); 
     def!(a2, int function(int,double,), ModuleName!"testing")(); 
     def!(a3, int function(int[]), ModuleName!"testing")(); 
-    add_module("testing");
+    on_py_init({
+            add_module!(ModuleName!"testing")();
+    });
+    py_init();
 }
 
 unittest{

File examples/pyd_unittests/def3.d

+import pyd.pyd, pyd.embedded;
+
+int a(int i) {
+    return 10;
+}
+
+int a(double d){
+    return 20;
+}
+
+int a2(int i, double d=4.5) {
+    return cast(int) (100*d + i);
+}
+
+int a3(int[] i...) {
+    int ret = 42;
+    foreach(_i; i) ret += _i;
+    return ret;
+}
+
+static this() {
+    def!(a,int function(double), ModuleName!"testing")(); 
+    def!(a2, int function(int,double,), ModuleName!"testing")(); 
+    def!(a3, int function(int[]), ModuleName!"testing")(); 
+    on_py_init({
+            add_module!(ModuleName!"testing")();
+    });
+    py_init();
+}
+
+unittest{
+    assert(py_eval!int("a(1.0)","testing") == 20);
+    assert(py_eval!int("a2(4,2.1)","testing") == 214);
+    assert(py_eval!int("a2(4)","testing") == 454);
+    assert(py_eval!int("a2(i=4)","testing") == 454);
+    assert(py_eval!int("a3(4)","testing") == 46);
+    assert(py_eval!int("a3(i=4)","testing") == 46);
+    assert(py_eval!int("a3(4,3)","testing") == 49);
+    assert(py_eval!int("a3(i=[4,3])","testing") == 49);
+}
+
+void main() {}

File examples/pyd_unittests/embedded.d

 import pyd.pyd, pyd.embedded;
 
 static this() {
-    add_module("testing");
+    on_py_init({
+    add_module!(ModuleName!"testing")();
+    });
+    py_init();
 }
 // py_def
 unittest {

File examples/pyd_unittests/embedded3.d

+import pyd.pyd, pyd.embedded;
+
+static this() {
+    on_py_init({
+    add_module!(ModuleName!"testing")();
+    });
+    py_init();
+}
+// py_def
+unittest {
+    alias py_def!(
+            "def func1(a):\n"
+            " return a*2+1",
+            "testing", 
+            int function(int)) func1;
+    assert(func1(1) == 3);    
+    assert(func1(2) == 5);    
+    assert(func1(3) == 7);    
+}
+
+void main() {}

File examples/pyd_unittests/func_wrap3.d

+
+import pyd.pyd, pyd.embedded;
+import pyd.func_wrap;
+import core.vararg;
+
+void foo0() {
+}
+
+void foo1(int i, int j) {
+}
+void foo2(int i, double j = 2.0) {
+}
+void foo3(...) {
+}
+void foo4(int[] i...) {
+}
+
+void foo5(int i=1, double d = 2.0) {
+}
+
+class Foo1{
+    this(int i, int j) {
+    }
+}
+class Foo2{
+    this(int i, double j = 1.0) {
+    }
+}
+class Foo3{
+    this(...){
+    }
+}
+class Foo4{
+    this(int[] i...){
+    }
+}
+
+unittest {
+    static assert(getparams!foo1 == "int i, int j");
+    static assert(getparams!foo2 == "int i, double j = 2");
+    static assert(getparams!foo3 == "...");
+    static assert(getparams!foo4 == "int[] i...");
+    auto fn = &call_ctor!(Foo2, Init!(int, double)).func;
+    //pragma(msg, typeof(fn));
+    //fn(1);
+    static assert(minArgs!(call_ctor!(Foo2, Init!(int, double)).func) == 1);
+
+    static assert(minArgs!foo1 == 2);
+    static assert(minArgs!foo2 == 1);
+    static assert(minArgs!foo3 == 0);
+    static assert(minArgs!foo4 == 0);
+
+    assert(supportsNArgs!foo0(0));
+    assert(!supportsNArgs!foo0(1));
+    assert(!supportsNArgs!foo0(2));
+    assert(!supportsNArgs!foo0(3));
+
+    assert(!supportsNArgs!foo1(0));
+    assert(!supportsNArgs!foo1(1));
+    assert(supportsNArgs!foo1(2));
+    assert(!supportsNArgs!foo1(3));
+
+    assert(!supportsNArgs!(Foo1.__ctor)(0));
+    assert(!supportsNArgs!(Foo1.__ctor)(1));
+    assert(supportsNArgs!(Foo1.__ctor)(2));
+    assert(!supportsNArgs!(Foo1.__ctor)(3));
+
+    assert(!supportsNArgs!foo2(0));
+    assert(supportsNArgs!foo2(1));
+    assert(supportsNArgs!foo2(2));
+    assert(!supportsNArgs!foo2(3));
+
+    assert(!supportsNArgs!(Foo2.__ctor)(0));
+    assert(supportsNArgs!(Foo2.__ctor)(1));
+    assert(supportsNArgs!(Foo2.__ctor)(2));
+    assert(!supportsNArgs!(Foo2.__ctor)(3));
+
+    assert(supportsNArgs!foo3(0));
+    assert(supportsNArgs!foo3(1));
+    assert(supportsNArgs!foo3(2));
+    assert(supportsNArgs!foo3(3));
+
+    assert(supportsNArgs!foo4(0));
+    assert(supportsNArgs!foo4(1));
+    assert(supportsNArgs!foo4(2));
+    assert(supportsNArgs!foo4(3));
+
+    assert(supportsNArgs!foo5(0));
+    assert(supportsNArgs!foo5(1));
+    assert(supportsNArgs!foo5(2));
+    assert(!supportsNArgs!foo5(3));
+
+}
+
+
+void main() {}

File examples/pyd_unittests/make_object.d

 import std.stdio;
 
 static this() {
-    add_module("testing");
+    on_py_init({
+    add_module!(ModuleName!"testing")();
+    });
+    py_init();
 }
 
 auto cantconvert(E)(lazy E e) {

File examples/pyd_unittests/makefile

 	     -version=Python_2_5_Or_Later \
 	     -version=Python_2_4_Or_Later \
 	     -L-lpython2.7
+PYTHON_3_2 = -version=Python_3_2_Or_Later \
+	     -version=Python_3_1_Or_Later \
+	     -version=Python_3_0_Or_Later \
+	     -version=Python_2_7_Or_Later \
+             -version=Python_2_6_Or_Later \
+	     -version=Python_2_5_Or_Later \
+	     -version=Python_2_4_Or_Later \
+	     -L-lpython3.2mu
 DC = dmd -unittest -property -debug -gc
 
-all: pydobject.x make_object.x embedded.x func_wrap.x class_wrap.x def.x struct_wrap.x buffer.x
+all: pydobject.x make_object.x make_object3.x embedded.x embedded3.x func_wrap.x func_wrap3.x class_wrap.x class_wrap3.x def.x def3.x struct_wrap.x buffer.x
 	./pydobject.x
 	./make_object.x
+	./make_object3.x
 	./embedded.x
+	./embedded3.x
 	./func_wrap.x
+	./func_wrap3.x
 	./class_wrap.x
+	./class_wrap3.x
 	./struct_wrap.x
 	./def.x
+	./def3.x
 	./buffer.x
 
 clean:
 	rm -f *.x
 	rm -f *.o
 
+%3.x: %3.d
+	$(DC) $(PYTHON_3_2) $(PYD_FILES) $^ -of$@ -I$(PYD_DIR)
 %.x: %.d
 	$(DC) $(PYTHON_2_7) $(PYD_FILES) $^ -of$@ -I$(PYD_DIR)
 

File examples/pyd_unittests/pydobject.d

 import pyd.pyd, pyd.embedded;
 
 static this() {
-    add_module("testing");
+    on_py_init({
+    add_module!(ModuleName!"testing")();
+    });
+    py_init();
 }
 
 // PydObject as dict

File examples/pyd_unittests/struct_wrap.d

 }
 
 static this() {
-    add_module("testing");
+    on_py_init({
+    add_module!(ModuleName!"testing")();
+    });
+    on_py_init({
     wrap_struct!(
             Foo1,
             ModuleName!"testing",
             Member!("k"),
             Def!(Foo1.bar),
             )();
+    }, PyInitOrdering.After);
+
+    py_init();
 }
 
 

File examples/pyind/makefile

 	     -L-lpython3.2mu
 DC = dmd -m64 -unittest -property -debug  
 #DC = ldmd2 -unittest -property -debug -gc
-all: pyind
+all: pyind pyind3
 clean:
 	rm -f *.o
 	rm -f pyind

File examples/pyind/pyind.d

 
 
 static this() {
-    def!(knock, ModuleName!"office", Docstring!"a brain specialist works here")(); 
-    add_module("office");
+    on_py_init({
+            def!(knock, ModuleName!"office", 
+                Docstring!"a brain specialist works here")(); 
+            add_module!(ModuleName!"office")();
+            });
+    py_init();
+
     wrap_class!(Y, 
         Def!(Y.query),
         ModuleName!"office",

File examples/pyind/pyind3.d

 
 
 static this() {
-    def!(knock, ModuleName!"office", Docstring!"a brain specialist works here")(); 
-    add_module("office");
+    on_py_init({
+            def!(knock, ModuleName!"office", Docstring!"a brain specialist works here")(); 
+            add_module!(ModuleName!"office")();
+    });
+    on_py_init({
     wrap_class!(Y, 
         Def!(Y.query),
         ModuleName!"office",
         Property!(Y.brain_status),
         Property!(Y.resolution, Mode!"r"),
     )();
+    }, PyInitOrdering.After);
+    py_init();
 }
 
 void main() {
-    py_stmts(
-            "import office\n"
-            "print(office)"
-            ,
-            );
     // simple expressions can be evaluated
     int i = py_eval!int("1+2", "office");
     writeln(i);
     py_stmts(q"<
 y = Y();
 y.brain_status = "HURTS";
-print "MY BRAIN %s" % y.brain_status;
-print y.resolution
+print("MY BRAIN %s" % y.brain_status);
+print(y.resolution)
 >","office");
 }
 

File infrastructure/deimos/python/import_.d

 __gshared PyTypeObject PyNullImporter_Type;
 __gshared _inittab* PyImport_Inittab;
 
-version(Python_2_7_Or_Later){
-    alias const(char) Char2;
-}else{
-    alias char Char2;
-}
-
 version(Python_3_0_Or_Later) {
-    int PyImport_AppendInittab(Char2* name, PyObject* function() initfunc);
+    int PyImport_AppendInittab(const(char)* name, PyObject* function() initfunc);
 }else {
-    int PyImport_AppendInittab(Char2* name, void function() initfunc);
+    int PyImport_AppendInittab(const(char)* name, void function() initfunc);
 }
 int PyImport_ExtendInittab(_inittab *newtab);
 

File infrastructure/deimos/python/object.d

     }
 }
 
-auto Py_REFCNT()(PyObject* ob){ return ob.ob_refcnt; }
-auto Py_TYPE()(PyObject* ob){ return ob.ob_type; }
-auto Py_SIZE()(PyVarObject* ob){ return ob.ob_size; }
+auto Py_REFCNT(T)(T* ob) { 
+    return (cast(PyObject*)ob).ob_refcnt; 
+}
+auto Py_TYPE(T)(T* ob) { 
+    return (cast(PyObject*)ob).ob_type; 
+}
+auto Py_SIZE(T)(T* ob) { 
+    return (cast(PyVarObject*)ob).ob_size; 
+}
+
+// nonstandard, but annonying to do without.
+void Py_SET_REFCNT(T)(T* ob, int refcnt) {
+    (cast(PyObject*) ob).ob_refcnt = refcnt;
+}
+void Py_SET_TYPE(T)(T* ob, PyTypeObject* tipo) { 
+    (cast(PyObject*)ob).ob_type = tipo; 
+}
+void Py_SET_SIZE(T)(T* ob, Py_ssize_t size) { 
+    (cast(PyVarObject*)ob).ob_size = size; 
+}
+
+// end nonstandard
 
 alias PyObject* function(PyObject*) unaryfunc;
 alias PyObject* function(PyObject*, PyObject*) binaryfunc;

File infrastructure/pyd/class_wrap.d

 import std.traits;
 import std.conv;
 import std.exception: enforce;
+import std.functional;
 import std.metastrings;
 import std.typetuple;
 import std.string: format;
 }
 
 void init_PyTypeObject(T)(ref PyTypeObject tipo) {
-    (cast(PyObject*) &tipo).ob_refcnt = 1;
+    Py_SET_REFCNT(&tipo, 1);
     tipo.tp_dealloc = &wrapped_methods!(T).wrapped_dealloc;
     tipo.tp_new = &wrapped_methods!(T).wrapped_new;
 }
     }
     static void call(T)() {
         alias wrapped_class_type!T type;
-        type.tp_compare = &opcmp_wrap!(T, Inner!T.FN).func;
+        type.tp_richcompare = &rich_opcmp_wrap!(T, Inner!T.FN).func;
     }
     template shim(size_t i,T) {
         // bah
             static if (is(typeof(new T))) {
                 static if (is(T == class)) {
                     type.tp_init = &wrapped_init!(Shim).init;
-                    import std.stdio;
-                    writeln("type.tp_init: ", type.tp_init);
                 } else {
                     type.tp_init = &wrapped_struct_init!(T).init;
                 }
 }
 
 /*
+   Extended slice syntax goes through mp_subscript, mp_ass_subscript,
+   not sq_slice, sq_ass_slice.
+*/
+struct IndexSliceMerge(Params...) {
+}
+
+/*
 Params: each param is a Type which supports the interface
 
 Param.needs_shim == false => Param.call!(T)
         alias _T* T;
     }
     void wrap_class() {
+        if(!Pyd_Module_p(modulename) && 
+                should_defer_class_wrap(modulename, name)) {
+            defer_class_wrap(modulename, name,  toDelegate(&wrap_class));
+            return;
+        }
         alias wrapped_class_type!(T) type;
         init_PyTypeObject!T(type);
 
             }
         }
 
-        assert(Pyd_Module_p(modulename) !is null, "Must initialize module before wrapping classes.");
+        assert(Pyd_Module_p(modulename) !is null, "Must initialize module '" ~ modulename ~ "' before wrapping classes.");
         string module_name = to!string(PyModule_GetName(Pyd_Module_p(modulename)));
 
         //////////////////
         // Basic values //
         //////////////////
-        (cast(PyObject*) &type).ob_type      = &PyType_Type;
+        Py_SET_TYPE(&type, &PyType_Type);
         type.tp_basicsize = (wrapped_class_object!(T)).sizeof;
         type.tp_doc       = (docstring ~ "\0").ptr;
         version(Python_3_0_Or_Later) {

File infrastructure/pyd/def.d

 }
 private PyObject*[string] pyd_modules;
 
+private void delegate()[string][string] pyd_module_classes;
+
 private void ready_module_methods(string modulename) {
     PyMethodDef empty;
     if (!(modulename in module_methods)) {
     }
 }
 
+
 PyObject* Pyd_Module_p(string modulename="") {
     PyObject** m = modulename in pyd_modules;
     if (m is null) return null;
     else return *m;
 }
 
+bool should_defer_class_wrap(string modulename, string classname) {
+    version(Python_3_0_Or_Later) {
+    return !(modulename in pyd_modules) && (modulename in pyd_module_classes)
+        && !(classname in pyd_module_classes[modulename]);
+    }else {
+        return false;
+    }
+}
+void defer_class_wrap(string modulename, string classname, 
+        void delegate() wrapper) {
+    pyd_module_classes[modulename][classname] = wrapper;
+}
+
 /// Param of def
 struct ModuleName(string _modulename) {
     enum modulename = _modulename;
 
 string pyd_module_name;
 
+/// For embedding python
+void py_init() {
+    foreach(action; before_py_init_deferred_actions) {
+        action();
+    }
+    Py_Initialize();
+    py_init_called = true;
+    foreach(action; after_py_init_deferred_actions) {
+        action();
+    }
+}
+
+/// For embedding python, should you wish to restart the interpreter.
+void py_finish() {
+    Py_Finalize();
+    py_init_called = false;
+}
+
 /**
  * Module initialization function. Should be called after the last call to def.
  * For extending python.
  */
 PyObject* module_init(string docstring="") {
-    //_loadPythonSupport();
     string name = pyd_module_name;
     ready_module_methods("");
     version(Python_3_0_Or_Later) {
         modl.m_size = -1;
         modl.m_methods = module_methods[""].ptr;
 
-        pyd_modules[""] = PyModule_Create(modl);
     }else {
         pyd_modules[""] = Py_INCREF(Py_InitModule3((name ~ "\0"), 
                     module_methods[""].ptr, (docstring ~ "\0")));
     }
-    foreach(action; on_module_init_deferred_actions) {
+    foreach(action; before_py_init_deferred_actions ~ 
+            after_py_init_deferred_actions) {
+        //TODO: will this work?
         action();
     }
-    module_init_called = true;
+    py_init_called = true;
     return pyd_modules[""];
 }
 
-/// For embedding python
-void py_init() {
-    Py_Initialize();
-    foreach(action; on_module_init_deferred_actions) {
-        action();
-    }
-    module_init_called = true;
-}
+/**
+Module initialization function. Should be called after the last call to def.
 
-/**
- * Module initialization function. Should be called after the last call to def.
+Params
+Options = Optional parameters. Takes Docstring!(docstring), and ModuleName!(modulename)
+modulename = name of module
+docstring = docstring of module
  */
-PyObject* add_module(string modulename, string docstring="") {
+void add_module(Options...)() {
+    alias Args!("","", "", "",Options) args;
+    enum modulename = args.modulename;
+    enum docstring = args.docstring;
     ready_module_methods(modulename);
     version(Python_3_0_Or_Later) {
-        PyModuleDef* modl = pyd_moduledefs[modulename] = new PyModuleDef;
-        (cast(PyObject*) modl).ob_refcnt = 1;
+        assert(!py_init_called);
+        PyModuleDef* modl = new PyModuleDef;
+        Py_SET_REFCNT(modl, 1);
         modl.m_name = zcc(modulename);
         modl.m_doc = zcc(docstring);
         modl.m_size = -1;
         modl.m_methods = module_methods[modulename].ptr;
+        pyd_moduledefs[modulename] = modl;
+        pyd_module_classes[modulename] = (void delegate()[string]).init;
 
-        pyd_modules[modulename] = PyModule_Create(modl);
-        zz = 44;
+        PyImport_AppendInittab(modulename.ptr, &Py3_ModuleInit!modulename.func);
     }else{
+        // schizophrenic arrangements, these
+        assert(py_init_called);
         pyd_modules[modulename] = Py_INCREF(Py_InitModule3((modulename ~ "\0"), 
                     module_methods[modulename].ptr, (docstring ~ "\0")));
     }
-    return pyd_modules[modulename];
 }
 
-bool module_init_called = false;
-void delegate()[] on_module_init_deferred_actions;
-void on_module_init(void delegate() dg) {
-    if(module_init_called) {
-        dg();
-    }else {
-        on_module_init_deferred_actions ~= dg;
+template Py3_ModuleInit(string modulename) {
+    extern(C) PyObject* func() {
+        pyd_modules[modulename] = PyModule_Create(pyd_moduledefs[modulename]);
+        foreach(n,action; pyd_module_classes[modulename]) {
+            action();
+        }
+        return pyd_modules[modulename];
     }
 }
 
+bool py_init_called = false;
+void delegate()[] before_py_init_deferred_actions;
+void delegate()[] after_py_init_deferred_actions;
+
+/// 
+enum PyInitOrdering{
+    /// call will be made before Py_Initialize.
+    Before,
+    /// call will be made after Py_Initialize.
+    After,
+}
+
+/// call will be made at the appropriate time for initializing
+/// modules.  (for python 2, it should be after Py_Initialize, 
+/// for python 3, before).
+version(Python_3_0_Or_Later) {
+    enum PyInitOrdering ModuleInit = PyInitOrdering.Before;
+}else{
+    enum PyInitOrdering ModuleInit = PyInitOrdering.After;
+}
+
+/**
+  Use this to wrap calls to add_module and the like.
+
+  py_init will ensure they are called at the appropriate time
+  */
+void on_py_init(void delegate() dg, 
+        PyInitOrdering ord = ModuleInit) {
+    with(PyInitOrdering) switch(ord) {
+        case Before:
+            if(py_init_called) {
+                enforce(0, "py_init has already been called");
+            }
+            before_py_init_deferred_actions ~= dg;
+            break;
+        case After:
+            if(py_init_called) {
+                dg();
+            }
+            after_py_init_deferred_actions ~= dg;
+            break;
+        default:
+            assert(0);
+    }
+}
+

File infrastructure/pyd/embedded.d

 import std.string: strip;
 import std.traits;
 
-shared static this() {
-    py_init();
-}
-
 /++
  + Fetch a python module object.
  +/
 PydObject py_import(string name) {
+    debug assert(Py_IsInitialized(), "python not initialized");
     return new PydObject(PyImport_ImportModule(zcc(name)));
 }
 
 func_t = type of d function
  +/
 ReturnType!func_t py_def( string python, string modl, func_t) 
-    (ParameterTypeTuple!func_t args, string file = __FILE__, size_t line = __LINE__) {
+    (ParameterTypeTuple!func_t args, 
+     string file = __FILE__, size_t line = __LINE__) {
     //Note that type is really the only thing that need be static here, but hey.
         alias ReturnType!func_t R;
         alias ParameterTypeTuple!func_t Args;
     static PythonException exc;
     static string errmsg;
     static bool once = true;
+    debug assert(Py_IsInitialized(), "python not initialized");
     if(once) {
         once = false;
         auto globals = py_import(modl).getdict();
 modl = context in which to run expression. either a python module name, or "".
  +/
 T py_eval(T = PydObject)(string python, string modl = "", string file = __FILE__, size_t line = __LINE__) {
+    debug assert(Py_IsInitialized(), "python not initialized");
     PydObject locals = null;
     PyObject* locals_ptr = null;
     if(modl == "") {
 modl = context in which to run expression. either a python module name, or "".
  +/
 void py_stmts(string python, string modl = "",string file = __FILE__, size_t line = __LINE__) {
+    debug assert(Py_IsInitialized(), "python not initialized");
     PydObject locals;
     PyObject* locals_ptr;
     if(modl == "") {

File infrastructure/pyd/exception.d

     string text;
     auto ptext = PyObject_GetAttrString(value, "text");
     if(ptext) {
+        version(Python_3_0_Or_Later) {
+            ptext = PyUnicode_AsUTF8String(ptext);
+        }
         auto p2text = PyBytes_AsString(ptext);
         if(p2text) text = strip(to!string(p2text));
     }
     string message;
     auto pmsg = PyObject_GetAttrString(value, "msg");
     if(pmsg) {
+        version(Python_3_0_Or_Later) {
+            pmsg = PyUnicode_AsUTF8String(pmsg);
+        }
         auto cmsg = PyBytes_AsString(pmsg);
         if(cmsg) message = to!string(cmsg);
     }

File infrastructure/pyd/make_object.d

 
 
 shared static this() {
-    on_module_init(
-            {
-            add_module("pyd", "contains some wrapper utilities");
+    on_py_init( {
+            add_module!(
+                ModuleName!"pyd", 
+                Docstring!"contains some wrapper utilities")();
+            });
+    on_py_init( {
             wrap_struct!(RangeWrapper,
                 ModuleName!"pyd",
                 Def!(RangeWrapper.iter, PyName!"__iter__"),
                 Def!(RangeWrapper.next))();
-            });
+            }, PyInitOrdering.After);
 }
 
 class to_conversion_wrapper(dg_t) {
         if (is_wrapped!(T) && PyObject_TypeCheck(o, &wrapped_class_type!(T))) {
             return WrapPyObject_AsObject!(T)(o);
         }// else could_not_convert!(T)(o);
-    /+
-    } else static if (is(wchar[] : T)) {
-        wchar[] temp;
-        temp.length = PyUnicode_GetSize(o);
-        PyUnicode_AsWideChar(cast(PyUnicodeObject*)o, temp, temp.length);
-        return temp;
-    +/
     } else static if (isSomeString!T) {
         alias Unqual!(typeof(T.init[0])) C;
         PyObject* str;

File infrastructure/pyd/op_wrap.d

     }
 }
 
+template rich_opcmp_wrap(T, alias fn) {
+    alias wrapped_class_object!(T) wrap_object;
+    alias ParameterTypeTuple!(fn) Info;
+    alias dg_wrapper!(T, typeof(&fn)) get_dg;
+    alias Info[0] OtherT;
+    extern(C)
+    PyObject* func(PyObject* self, PyObject* other, int op) {
+        return exception_catcher(delegate PyObject*() {
+            auto dg = get_dg((cast(wrap_object*)self).d_obj, &fn);
+            auto dother = python_to_d!OtherT(other);
+            int result = dg(dother);
+            bool pyresult;
+            switch(op) {
+                case Py_LT:
+                    pyresult = (result < 0);
+                    break;
+                case Py_LE:
+                    pyresult = (result <= 0);
+                    break;
+                case Py_EQ:
+                    pyresult = (result == 0);
+                    break;
+                case Py_NE:
+                    pyresult = (result != 0);
+                    break;
+                case Py_GT:
+                    pyresult = (result > 0);
+                    break;
+                case Py_GE:
+                    pyresult = (result >= 0);
+                    break;
+                default:
+                    assert(0);
+            }
+            if (pyresult) return Py_INCREF(Py_True);
+            else return Py_INCREF(Py_False);
+        });
+    }
+}
+
 //----------//
 // Dispatch //
 //----------//