@@ -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