fix(bytes): make bytes object iterable

Add __iter__ method to bytes type and implement bytes_iterator type.
This allows iterating over bytes objects to get individual byte values
as integers (0-255), matching Python's behavior.

Changes:
- include/pocketpy/objects/iterator.h: Add bytes_iterator struct
- include/pocketpy/pocketpy.h: Add tp_bytes_iterator type constant
- include/pocketpy/interpreter/vm.h: Declare pk_bytes_iterator__register
- src/bindings/py_str.c: Implement bytes__iter__ and bytes_iterator__next__
- src/interpreter/vm.c: Register bytes_iterator type

Fixes #450
This commit is contained in:
wdsmini 2026-03-20 11:28:08 +08:00
parent e98bd53b88
commit 89e8341d87
5 changed files with 39 additions and 0 deletions

View File

@ -133,6 +133,7 @@ void pk_number__register();
py_Type pk_str__register(); py_Type pk_str__register();
py_Type pk_str_iterator__register(); py_Type pk_str_iterator__register();
py_Type pk_bytes__register(); py_Type pk_bytes__register();
py_Type pk_bytes_iterator__register();
py_Type pk_dict__register(); py_Type pk_dict__register();
py_Type pk_dict_items__register(); py_Type pk_dict_items__register();
py_Type pk_list__register(); py_Type pk_list__register();

View File

@ -13,3 +13,9 @@ typedef struct list_iterator {
c11_vector* vec; c11_vector* vec;
int index; int index;
} list_iterator; } list_iterator;
typedef struct bytes_iterator {
unsigned char* data;
int length;
int index;
} bytes_iterator;

View File

@ -840,6 +840,7 @@ enum py_PredefinedType {
tp_BaseException, tp_BaseException,
tp_Exception, tp_Exception,
tp_bytes, tp_bytes,
tp_bytes_iterator,
tp_namedict, tp_namedict,
tp_locals, tp_locals,
tp_code, tp_code,

View File

@ -796,6 +796,28 @@ static bool bytes__len__(int argc, py_Ref argv) {
return true; return true;
} }
static bool bytes__iter__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
c11_bytes* self = py_touserdata(&argv[0]);
bytes_iterator* ud = py_newobject(py_retval(), tp_bytes_iterator, 1, sizeof(bytes_iterator));
ud->data = self->data;
ud->length = self->size;
ud->index = 0;
py_setslot(py_retval(), 0, argv); // keep a reference to the bytes object
return true;
}
static bool bytes_iterator__next__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
bytes_iterator* ud = py_touserdata(argv);
if(ud->index < ud->length) {
py_newint(py_retval(), ud->data[ud->index]);
ud->index++;
return true;
}
return StopIteration();
}
py_Type pk_bytes__register() { py_Type pk_bytes__register() {
py_Type type = pk_newtype("bytes", tp_object, NULL, NULL, false, true); py_Type type = pk_newtype("bytes", tp_object, NULL, NULL, false, true);
// no need to dtor because the memory is controlled by the object // no need to dtor because the memory is controlled by the object
@ -808,9 +830,17 @@ py_Type pk_bytes__register() {
py_bindmagic(tp_bytes, __add__, bytes__add__); py_bindmagic(tp_bytes, __add__, bytes__add__);
py_bindmagic(tp_bytes, __hash__, bytes__hash__); py_bindmagic(tp_bytes, __hash__, bytes__hash__);
py_bindmagic(tp_bytes, __len__, bytes__len__); py_bindmagic(tp_bytes, __len__, bytes__len__);
py_bindmagic(tp_bytes, __iter__, bytes__iter__);
py_bindmethod(tp_bytes, "decode", bytes_decode); py_bindmethod(tp_bytes, "decode", bytes_decode);
return type; return type;
} }
py_Type pk_bytes_iterator__register() {
py_Type type = pk_newtype("bytes_iterator", tp_object, NULL, NULL, false, true);
py_bindmagic(type, __iter__, pk_wrapper__self);
py_bindmagic(type, __next__, bytes_iterator__next__);
return type;
}
#undef DEF_STR_CMP_OP #undef DEF_STR_CMP_OP

View File

@ -156,6 +156,7 @@ void VM__ctor(VM* self) {
validate(tp_BaseException, pk_BaseException__register()); validate(tp_BaseException, pk_BaseException__register());
validate(tp_Exception, pk_Exception__register()); validate(tp_Exception, pk_Exception__register());
validate(tp_bytes, pk_bytes__register()); validate(tp_bytes, pk_bytes__register());
validate(tp_bytes_iterator, pk_bytes_iterator__register());
validate(tp_namedict, pk_namedict__register()); validate(tp_namedict, pk_namedict__register());
validate(tp_locals, pk_newtype("locals", tp_object, NULL, NULL, false, true)); validate(tp_locals, pk_newtype("locals", tp_object, NULL, NULL, false, true));
validate(tp_code, pk_code__register()); validate(tp_code, pk_code__register());