Skip to content

Commit

Permalink
Add super and self methods (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
robert3005 authored Sep 5, 2023
1 parent 51dc3dd commit d6da8dc
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 7 deletions.
36 changes: 35 additions & 1 deletion example/classes.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,40 @@ const py = @import("pydust");

pub const Animal = py.class("Animal", struct {
pub const __doc__ = "Animal docstring";

const Self = @This();

kind: u64,

pub fn __init__(self: *Self, args: *const extern struct { kind: py.PyLong }) !void {
self.kind = try args.kind.as(u64);
}

pub fn get_kind(self: *Self) !py.PyLong {
return py.PyLong.from(u64, self.kind);
}

pub fn get_kind_name(self: *Self) !py.PyString {
return switch (self.kind) {
1 => py.PyString.fromSlice("Dog"),
2 => py.PyString.fromSlice("Cat"),
3 => py.PyString.fromSlice("Parrot"),
else => py.RuntimeError.raise("Unknown animal kind"),
};
}
});

pub const Dog = py.subclass("Dog", &.{Animal}, struct {
pub const __doc__ = "Adorable animal docstring";
const Self = @This();

animal: Animal,
name: py.PyString,

pub fn __init__(self: *Self, args: *const extern struct { name: py.PyString }) !void {
var kind = try py.PyLong.from(u64, 1);
defer kind.decref();
try Animal.__init__(&self.animal, &.{ .kind = kind });
args.name.incref();
self.name = args.name;
}
Expand All @@ -27,12 +52,21 @@ pub const Dog = py.subclass("Dog", &.{Animal}, struct {
pub fn make_noise() !py.PyString {
return py.PyString.fromSlice("Bark!");
}

pub fn get_kind_name(self: *Self) !py.PyString {
var super = try py.super(Dog, self);
var superKind = try super.get("get_kind_name");
var kindStr = py.PyString.of(try superKind.call0());
kindStr = try kindStr.appendSlice(" named ");
kindStr = try kindStr.append(self.name);
return kindStr;
}
});

pub const Owner = py.class("Owner", struct {
pub const __doc__ = "Takes care of an animal";

pub fn adopt_puppy(args: *const extern struct { name: py.PyString }) !py.PyObject {
pub fn name_puppy(args: *const extern struct { name: py.PyString }) !py.PyObject {
return try py.init(Dog, .{ .name = args.name });
}
});
Expand Down
17 changes: 17 additions & 0 deletions pydust/src/pydust.zig
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub fn init(comptime Cls: type, args: ?InitArgs(Cls)) !types.PyObject {
}
}

/// Find the type of the positional args for a class
fn InitArgs(comptime Cls: type) type {
if (!@hasDecl(Cls, "__init__")) {
return Cls;
Expand All @@ -139,6 +140,22 @@ fn InitArgs(comptime Cls: type) type {
return @typeInfo(sig.argsParam.?.type.?).Pointer.child;
}

/// Convert user state instance into PyObject instance
pub fn self(selfInstance: anytype) !types.PyObject {
const selfState = @fieldParentPtr(pytypes.State(@typeInfo(@TypeOf(selfInstance)).Pointer.child), "state", selfInstance);
return .{ .py = &selfState.obj };
}

/// Get zig state of super class `Super` of `classSelf` parameter
pub fn super(comptime Super: type, selfInstance: anytype) !types.PyObject {
const imported = try types.PyModule.import(findContainingModule(Super));
const superPyType = try imported.obj.get(getClassName(Super));
const pyObj = try self(selfInstance);

const superTypeObj = types.PyObject{ .py = @alignCast(@ptrCast(&ffi.PySuper_Type)) };
return superTypeObj.callArgs(.{ superPyType, pyObj });
}

/// Find the name of the module that contains the given definition.
pub fn getClassName(comptime definition: type) [:0]const u8 {
inline for (State.classes()) |classDef| {
Expand Down
4 changes: 4 additions & 0 deletions pydust/src/types/obj.zig
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ pub const PyObject = extern struct {
return .{ .py = ffi.PyObject_CallNoArgs(self.py) orelse return PyError.Propagate };
}

pub fn callArgs(self: PyObject, args: anytype) !PyObject {
return self.call(args, null);
}

pub fn call(self: PyObject, args: anytype, kwargs: anytype) !PyObject {
_ = kwargs;
const argsTuple = try py.PyTuple.from(args);
Expand Down
14 changes: 8 additions & 6 deletions test/test_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@

def test_hierarchy():
assert issubclass(classes.Dog, classes.Animal)
assert isinstance(classes.Dog("Pupper's name"), classes.Animal)
assert isinstance(classes.Dog("Dug"), classes.Animal)


def test_make_noise():
with pytest.raises(AttributeError):
classes.Animal().make_noise()
assert classes.Dog("Pupper's name").make_noise() == "Bark!"
classes.Animal(0).make_noise()
assert classes.Dog("Dug").make_noise() == "Bark!"


def test_adopt():
def test_super():
owner = classes.Owner()
adopted = owner.adopt_puppy("Cute pupper's name")
adopted = owner.name_puppy("Dug")
assert isinstance(adopted, classes.Dog)
assert adopted.get_name() == "Cute pupper's name"
assert adopted.get_name() == "Dug"
assert adopted.get_kind_name() == "Dog named Dug"
assert adopted.get_kind() == 1

0 comments on commit d6da8dc

Please sign in to comment.