Skip to content

Commit dc0923a

Browse files
committed
Use _ fns for incref/decref, add refcnt, remove uncessary type checks, fix str data
1 parent ee15a32 commit dc0923a

File tree

1 file changed

+90
-34
lines changed

1 file changed

+90
-34
lines changed

py.zig

Lines changed: 90 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -429,17 +429,25 @@ pub inline fn ObjectProtocol(comptime T: type) type {
429429
pub const IS_PYOBJECT = true;
430430

431431
pub inline fn incref(self: *T) void {
432-
c.Py_IncRef(@ptrCast(self));
432+
// Use the _ variant since it should be non-null
433+
c._Py_IncRef(@ptrCast(self));
433434
}
434435

435436
pub inline fn decref(self: *T) void {
436-
c.Py_DecRef(@ptrCast(self));
437+
// Use the _ variant since it should be non-null
438+
c._Py_DecRef(@ptrCast(self));
437439
}
438440

439441
// Create a new strong reference to an object: call Py_INCREF()
440442
// on o and return the object o.
441443
pub inline fn newref(self: *T) *T {
442-
return @ptrCast(c.Py_NewRef(@ptrCast(self)));
444+
self.incref();
445+
return self;
446+
}
447+
448+
// Get the object refcount
449+
pub inline fn refcnt(self: *const T) isize {
450+
return c.Py_REFCNT(@constCast(@ptrCast(self)));
443451
}
444452

445453
// Returns a borrwed reference to the type
@@ -574,43 +582,55 @@ pub inline fn ObjectProtocol(comptime T: type) type {
574582
// already an iterator. Raises TypeError and returns NULL if the object cannot be iterated.
575583
pub inline fn iter(self: *T) !*Iter {
576584
if (self.iterUnchecked()) |r| {
577-
if (!Iter.check(r)) {
578-
try typeError("iter did not return an iterator", .{});
579-
}
580-
return @ptrCast(r);
585+
return r;
581586
}
582587
return error.PyError;
583588
}
584589

585-
pub inline fn iterUnchecked(self: *T) ?*Object {
590+
pub inline fn iterUnchecked(self: *T) ?*Iter {
591+
// Python type checks the result is an Iter
586592
return @ptrCast(c.PyObject_GetIter(@ptrCast(self)));
587593
}
588594

589595
// Compute a string representation of object o. Null and type check the result is a Str.
590596
pub inline fn str(self: *T) !*Str {
591597
if (self.strUnchecked()) |s| {
592-
if (!Str.check(s)) {
593-
try typeError("str did not return a str", .{});
594-
}
595-
return @ptrCast(s);
598+
return s;
596599
}
597600
return error.PyError;
598601
}
599602

600603
// Calls PyObject_Str(self). Compute a string representation of object o.
601604
// Returns the string representation on success, NULL on failure.
602-
pub inline fn strUnchecked(self: *T) ?*Object {
605+
pub inline fn strUnchecked(self: *T) ?*Str {
606+
// Python type checks the result is a Str
603607
return @ptrCast(c.PyObject_Str(@ptrCast(self)));
604608
}
605609

606610
// Compute a bytes representation of object o. NULL is returned on failure and a bytes object on
607611
// success. This is equivalent to the Python expression bytes(o), when o is not an integer.
608612
// Unlike bytes(o), a TypeError is raised when o is an integer instead of a zero-initialized bytes object.
609-
pub inline fn bytes(self: *T) ?*Object {
613+
pub inline fn bytes(self: *T) ?*Bytes {
614+
if (self.bytesUnchecked()) |s| {
615+
return s;
616+
}
617+
return error.PyError;
618+
}
619+
620+
pub inline fn bytesUnchecked(self: *T) ?*Bytes {
621+
// Python type checks the result is Bytes
610622
return @ptrCast(c.PyObject_Bytes(@ptrCast(self)));
611623
}
612624

613-
pub inline fn repr(self: *T) ?*Object {
625+
pub inline fn repr(self: *T) ?*Str {
626+
if (self.reprUnchecked()) |s| {
627+
return s;
628+
}
629+
return error.PyError;
630+
}
631+
632+
pub inline fn reprUnchecked(self: *T) ?*Str {
633+
// Python type checks the result is a Str
614634
return @ptrCast(c.PyObject_Repr(@ptrCast(self)));
615635
}
616636

@@ -791,7 +811,7 @@ pub inline fn ObjectProtocol(comptime T: type) type {
791811
_ = fmt;
792812
_ = options;
793813
if (comptime T == Str) {
794-
try writer.print("{s}", .{self.data()});
814+
try writer.print("{s}", .{Str.data(@constCast(self))});
795815
} else {
796816
const s = try str(@constCast(self));
797817
defer s.decref();
@@ -1017,14 +1037,14 @@ pub fn IteratorProtocol(comptime T: type) type {
10171037
// (it is up to the caller to check this). If there are no remaining values, it returns null
10181038
// If an error occurs while retrieving the item, it throws error.PyError
10191039
// Returns new reference
1020-
pub fn next(self: *T) !?*Object {
1040+
pub inline fn next(self: *T) !?*Object {
10211041
if (self.nextUnchecked()) |r| {
1022-
return @ptrCast(r);
1042+
return r;
10231043
}
10241044
return checkErrorOccurred();
10251045
}
10261046

1027-
pub fn nextUnchecked(self: *T) ?*Object {
1047+
pub inline fn nextUnchecked(self: *T) ?*Object {
10281048
return @ptrCast(c.PyIter_Next(@ptrCast(self)));
10291049
}
10301050
};
@@ -1446,6 +1466,7 @@ pub const Str = extern struct {
14461466
}
14471467
const str = std.fmt.allocPrint(alloc, msg, args) catch {
14481468
try memoryError(); // TODO: This squashes the error
1469+
unreachable;
14491470
};
14501471
defer allocator.free(str);
14511472
// TODO: is there a way to avoid a copy?
@@ -1468,10 +1489,24 @@ pub const Str = extern struct {
14681489
c.PyUnicode_InternInPlace(@ptrCast(str));
14691490
}
14701491

1471-
// Return data as utf8
1472-
pub inline fn data(self: *const Str) [:0]const u8 {
1492+
// Return data as utf8. Ignores any errors decode error occurrs
1493+
pub inline fn data(self: *Str) [:0]const u8 {
14731494
std.debug.assert(Str.check(@ptrCast(self)));
1474-
return std.mem.span(c.PyUnicode_AsUTF8(@constCast(@ptrCast(self))));
1495+
return self.dataOrError() catch {
1496+
errorClear();
1497+
return "";
1498+
};
1499+
}
1500+
1501+
// Return data as utf8.
1502+
// Raises error if a decode error occurs or self is not a str.
1503+
pub inline fn dataOrError(self: *Str) ![:0]const u8 {
1504+
var size: isize = 0;
1505+
if (c.PyUnicode_AsUTF8AndSize(@ptrCast(self), &size)) |ptr| {
1506+
// The returned buffer always has an extra null byte appended (not included in size),
1507+
return @ptrCast(ptr[0..@intCast(size)]);
1508+
}
1509+
return error.PyError;
14751510
}
14761511
};
14771512

@@ -1500,10 +1535,31 @@ pub const Bytes = extern struct {
15001535
return error.PyError;
15011536
}
15021537

1538+
pub inline fn size(self: *Bytes) !usize {
1539+
const r = self.sizeUnchecked();
1540+
if (r < 0) {
1541+
return error.PyError;
1542+
}
1543+
return @intCast(r);
1544+
}
1545+
1546+
pub inline fn sizeUnchecked(self: *Bytes) isize {
1547+
return c.PyBytes_Size(@ptrCast(self));
1548+
}
1549+
15031550
// Return data as bytes
15041551
pub inline fn data(self: *Bytes) [:0]const u8 {
15051552
std.debug.assert(Bytes.check(@ptrCast(self)));
1506-
return std.mem.span(c.PyBytes_AsString(@ptrCast(self)));
1553+
return self.dataOrError() catch unreachable;
1554+
}
1555+
1556+
// Return data as bytes, raising an error if the type is invalid
1557+
pub inline fn dataOrError(self: *Bytes) ![:0]const u8 {
1558+
if (c.PyBytes_AsString(@ptrCast(self))) |ptr| {
1559+
const n: usize = @intCast(self.sizeUnchecked());
1560+
return @ptrCast(ptr[0..n]);
1561+
}
1562+
return error.PyError;
15071563
}
15081564
};
15091565

@@ -2316,7 +2372,7 @@ pub const Set = extern struct {
23162372
}
23172373

23182374
// Create a copy of this set
2319-
pub fn copy(self: *Set) !*Set {
2375+
pub inline fn copy(self: *Set) !*Set {
23202376
return try new(@ptrCast(self));
23212377
}
23222378

@@ -2339,7 +2395,7 @@ pub const Set = extern struct {
23392395
// Unlike the Python __contains__() method, this function does not automatically convert unhashable sets into temporary frozensets.
23402396
// Raise a TypeError if the key is unhashable.
23412397
// Raise SystemError if anyset is not a set, frozenset, or an instance of a subtype.
2342-
pub fn contains(self: *Set, key: *Object) !bool {
2398+
pub inline fn contains(self: *Set, key: *Object) !bool {
23432399
const r = self.containsUnchecked(key);
23442400
if (r < 0) {
23452401
return error.PyError;
@@ -2348,7 +2404,7 @@ pub const Set = extern struct {
23482404
}
23492405

23502406
// Same as contains with no error checking
2351-
pub fn containsUnchecked(self: *Set, key: *Object) c_int {
2407+
pub inline fn containsUnchecked(self: *Set, key: *Object) c_int {
23522408
return c.PySet_Contains(@ptrCast(self), @ptrCast(key));
23532409
}
23542410

@@ -2357,14 +2413,14 @@ pub const Set = extern struct {
23572413
// Raise a TypeError if the key is unhashable.
23582414
// Raise a MemoryError if there is no room to grow.
23592415
// Raise a SystemError if set is not an instance of set or its subtype.
2360-
pub fn add(self: *Set, key: *Object) !void {
2416+
pub inline fn add(self: *Set, key: *Object) !void {
23612417
if (self.addUnchecked(key) < 0) {
23622418
return error.PyError;
23632419
}
23642420
}
23652421

23662422
// Same as add with no error checking
2367-
pub fn addUnchecked(self: *Set, key: *Object) c_int {
2423+
pub inline fn addUnchecked(self: *Set, key: *Object) c_int {
23682424
return c.PySet_Add(@ptrCast(self), @ptrCast(key));
23692425
}
23702426

@@ -2373,7 +2429,7 @@ pub const Set = extern struct {
23732429
// Raise a TypeError if the key is unhashable.
23742430
// Unlike the Python discard() method, this function does not automatically convert unhashable sets into temporary frozensets.
23752431
// Raise SystemError if set is not an instance of set or its subtype.
2376-
pub fn discard(self: *Set, key: *Object) !bool {
2432+
pub inline fn discard(self: *Set, key: *Object) !bool {
23772433
const r = self.discardUnchecked(key);
23782434
if (r < 0) {
23792435
return error.PyError;
@@ -2382,35 +2438,35 @@ pub const Set = extern struct {
23822438
}
23832439

23842440
// Same as pop with no error checking
2385-
pub fn discardUnchecked(self: *Set, key: *Object) c_int {
2441+
pub inline fn discardUnchecked(self: *Set, key: *Object) c_int {
23862442
return c.PySet_Discard(@ptrCast(self), @ptrCast(key));
23872443
}
23882444

23892445
// Return a new reference to an arbitrary object in the set, and removes the object from the set.
23902446
// Raise KeyError if the set is empty. Raise a SystemError if set is not an instance of set or its subtype.
23912447
// Returns new reference
2392-
pub fn pop(self: *Set) !*Object {
2448+
pub inline fn pop(self: *Set) !*Object {
23932449
if (self.popUnchecked()) |r| {
23942450
return r;
23952451
}
23962452
return error.PyError;
23972453
}
23982454

23992455
// Same as pop with no error checking
2400-
pub fn popUnchecked(self: *Set) ?*Object {
2456+
pub inline fn popUnchecked(self: *Set) ?*Object {
24012457
return @ptrCast(c.PySet_Pop(@ptrCast(self)));
24022458
}
24032459

24042460
// Empty an existing set of all elements.
24052461
// raise SystemError if set is not an instance of set or its subtype.
2406-
pub fn clear(self: *Set) !void {
2462+
pub inline fn clear(self: *Set) !void {
24072463
if (self.clearUnchecked() < 0) {
24082464
return error.PyError;
24092465
}
24102466
}
24112467

24122468
// Same as clear with no error checking
2413-
pub fn clearUnchecked(self: *Set) c_int {
2469+
pub inline fn clearUnchecked(self: *Set) c_int {
24142470
return c.PySet_Clear(@ptrCast(self));
24152471
}
24162472
};

0 commit comments

Comments
 (0)