Pyd

Connecting D and Python

http://pyd.dsource.org/




Simple Example

import pyd.pyd;
import std.stdio;

void hello() {
    writefln("Hello, world!");
}

extern(C) void PydMain() {
    def!(hello);
    module_init();
}

And in Python:

>>> hello()
Hello, world!

Compiling Pyd

Pyd extends distutils:

# setup.py
from celerid.support \
    import setup, Extension

setup(
    name='foo',
    ext_modules=[
        Extension(
            'foo', ['foo.d']
        )
    ],
)

Compiling is as easy as:

python setup.py build

Functions

Exposing simple functions to Python is simple:

int func(int i) {
    return i * 2;
}

extern(C) void PydMain() {
    def!(func);
    module_init();
}
>>> func(10)
20

More complicated functions are still simple:

int func2(int i, int j=2) {
    return i * j;
}

extern(C) void PydMain() {
    def!(func2);
    module_init();
}

This works as expected:

>>> func2(1)
2
>>> func2(2, 3)
6

We can also wrap overloaded functions:

void func3(double d) {}
void func3(int i) {}

extern(C) void PydMain() {
    def!(func3, "func3a",
        void function(double));
    def!(func3, "func3b",
        void function(int));
    module_init();
}
>>> func3a(2.0)
>>> func3b(5)

PydObject

We can handle any arbitrary Python object as a PydObject:

PydObject add(PydObject a, PydObject b) {
    return a + b;
}

extern(C) void PydMain() {
    def!(add);
    module_init();
}
>>> add(10, 20)
30
>>> add('abc', 'def')
'abcdef'

Exceptions

Exceptions are safely handled:

void throw_something() {
    throw new Exception("Boo!");
}

extern(C) void PydMain() {
    def!(throw_something);
    module_init();
}
>>> throw_something()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: D Exception: object.Exception: Boo!

Exceptions caused by PydObject are handled, too:

>>> add(1, 'twelve')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Manual conversion

struct S {
    int i;
}

S func1() {
    S s;
    s.i = 50;
    return s;
}

void func2(S s) {
    writefln(s.i);
}

extern(C) void PydMain() {
    d_to_python(delegate int(S s) {
        return s.i;
    });
    python_to_d(delegate S(int i) {
        S s;
        s.i = i;
        return s;
    });
    def!(func1);
    def!(func2);
    module_init();
}
>>> func1()
50
>>> func2(20)
20

Classes

A Simple Example

class Foo {
    void bar() {}
}

extern(C) void PydMain() {
    module_init();
    wrap_class!(
        Foo,
        Def!(Foo.bar)
    );
}
>>> f = Foo()
>>> f.bar()

Functions can use wrapped classes:

class Foo {}

void func1(Foo f) {}
Foo func2() { return new Foo; }

extern(C) void PydMain() {
    def!(func1);
    def!(func2);
    module_init();
    wrap_class!(Foo);
}
>>> f = Foo()
>>> func1(f)
>>> g = func2()

Returned objects keep their identity:

Foo bar() {
    static Foo f;
    if (!f) { f = new Foo; }
    return f;
}
>>> a = bar(); b = bar()
>>> a is b
True

Inherited methods are wrapped automatically:

class Base {
    void foo() {
        writefln("Base.foo");
    }
}

class Derived : Base {}

extern(C) void PydMain() {
    module_init();
    wrap_class!(
        Base,
        Def!(Base.foo)
    );
    wrap_class!(Derived);
}

This works as expected in Python:

>>> d = Derived()
>>> d.foo()
Base.foo

Returned objects always keep their real type:

Base foo() {
    return new Derived;
}
>>> o = foo()
>>> type(o)
<type 'test.Derived'>

Operator overloads are wrapped automatically:

class Foo {
    int a;
    this(int a) { this.a = a; }
    int opAdd(Foo f) {
        return this.a + f.a;
    }
}

extern(C) void PydMain() {
    module_init();
    wrap_class!(
        Foo,
        Init!(void function(int))
    );
}
>>> Foo(1) + Foo(2)
3

You can even subclass D classes in Python.

>>> class PyDerived(Base):
...     def foo(self):
...         print "PyDerived.foo"
>>> o = PyDerived()
>>> o.foo()
PyDerived.foo

And they can be passed back to D:

void polymorphic_call(Base b) {
    b.foo();
}
>>> polymorphic_call(o)
PyDerived.foo