Compare commits

...

8 Commits

Author SHA1 Message Date
blueloveTH
1e17b49403 Update cute_png.c 2025-12-09 16:44:41 +08:00
blueloveTH
550f8c9d0e minor fix 2025-12-09 16:31:50 +08:00
blueloveTH
0c004a9a16 Create io.pyi 2025-12-09 16:13:49 +08:00
blueloveTH
2684038ccf backup 2025-12-09 16:04:38 +08:00
blueloveTH
a094dcb34d Update vm.c 2025-12-09 14:47:58 +08:00
blueloveTH
3731efba5c Update vm.c 2025-12-09 14:47:04 +08:00
blueloveTH
6cad72453b fix error of "A() takes no arguments" 2025-12-09 14:45:08 +08:00
blueloveTH
5c7fb79a14 support del slice for list 2025-12-09 13:43:33 +08:00
9 changed files with 155 additions and 27 deletions

View File

@ -1,4 +1,5 @@
#include "pocketpy.h"
#include <stdio.h>
#define CUTE_PNG_IMPLEMENTATION
#include "cute_png.h"
@ -9,7 +10,7 @@ static bool cute_png_loads(int argc, py_Ref argv) {
int size;
unsigned char* data = py_tobytes(argv, &size);
cp_image_t image = cp_load_png_mem(data, size);
if(image.pix == NULL) return ValueError("cute_png: %s", cp_error_reason);
if(image.pix == NULL) return ValueError("cp_load_png_mem() failed");
py_newarray2d(py_retval(), image.w, image.h);
for(int y = 0; y < image.h; y++) {
for(int x = 0; x < image.w; x++) {
@ -73,7 +74,19 @@ static bool cute_png_Image__from_bytes_STATIC(int argc, py_Ref argv) {
int size;
unsigned char* data = py_tobytes(argv, &size);
cp_image_t image = cp_load_png_mem(data, size);
if(image.pix == NULL) return ValueError("cute_png: %s", cp_error_reason);
if(image.pix == NULL) return ValueError("cp_load_png_mem() failed");
cp_image_t* ud =
py_newobject(py_retval(), py_gettype("cute_png", py_name("Image")), 0, sizeof(cp_image_t));
*ud = image;
return true;
}
static bool cute_png_Image__from_file_STATIC(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
PY_CHECK_ARG_TYPE(0, tp_str);
const char* path = py_tostr(argv);
cp_image_t image = cp_load_png(path);
if(image.pix == NULL) return ValueError("cp_load_png() failed");
cp_image_t* ud =
py_newobject(py_retval(), py_gettype("cute_png", py_name("Image")), 0, sizeof(cp_image_t));
*ud = image;
@ -147,10 +160,41 @@ static bool cute_png_Image__clear(int argc, py_Ref argv) {
return true;
}
static bool cute_png_Image__to_rgb565_bytes(int argc, py_Ref argv) {
static bool cute_png_Image__to_png_bytes(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
cp_image_t* image = py_touserdata(argv);
unsigned char* data = py_newbytes(py_retval(), image->w * image->h * 2);
cp_saved_png_t saved_image = cp_save_png_to_memory(image);
assert(saved_image.data != NULL);
unsigned char* data = py_newbytes(py_retval(), saved_image.size);
memcpy(data, saved_image.data, saved_image.size);
CUTE_PNG_FREE(saved_image.data);
return true;
}
static bool cute_png_Image__to_png_file(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_str);
cp_image_t* image = py_touserdata(argv);
const char* path = py_tostr(py_arg(1));
FILE* fp = fopen(path, "wb");
if(fp == NULL) return OSError("cannot open file '%s' for writing", path);
cp_saved_png_t saved_image = cp_save_png_to_memory(image);
assert(saved_image.data != NULL);
size_t size = fwrite(saved_image.data, saved_image.size, 1, fp);
CUTE_PNG_FREE(saved_image.data);
fclose(fp);
py_newint(py_retval(), size);
return true;
}
static bool cute_png_Image__to_rgb565_file(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_str);
cp_image_t* image = py_touserdata(argv);
const char* path = py_tostr(py_arg(1));
FILE* fp = fopen(path, "wb");
if(fp == NULL) return OSError("cannot open file '%s' for writing", path);
size_t size = 0;
for(int y = 0; y < image->h; y++) {
for(int x = 0; x < image->w; x++) {
size_t idx = y * image->w + x;
@ -160,21 +204,15 @@ static bool cute_png_Image__to_rgb565_bytes(int argc, py_Ref argv) {
uint16_t b = (pixel.b >> 3) & 0x1F;
uint16_t rgb565 = (r << 11) | (g << 5) | b;
// use little-endian
data[idx * 2 + 0] = rgb565 & 0xFF;
data[idx * 2 + 1] = (rgb565 >> 8) & 0xFF;
}
}
size_t delta = fwrite(&rgb565, 1, 2, fp);
size += delta;
if(delta != 2) {
py_newint(py_retval(), size);
return true;
}
static bool cute_png_Image__to_png_bytes(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
cp_image_t* image = py_touserdata(argv);
cp_saved_png_t saved_image = cp_save_png_to_memory(image);
assert(saved_image.data != NULL);
unsigned char* data = py_newbytes(py_retval(), saved_image.size);
memcpy(data, saved_image.data, saved_image.size);
CUTE_PNG_FREE(saved_image.data);
}
}
}
py_newint(py_retval(), size);
return true;
}
@ -188,6 +226,7 @@ void pk__add_module_cute_png() {
py_bindmethod(tp_image, "__new__", cute_png_Image__new__);
py_bindstaticmethod(tp_image, "from_bytes", cute_png_Image__from_bytes_STATIC);
py_bindstaticmethod(tp_image, "from_file", cute_png_Image__from_file_STATIC);
py_bindproperty(tp_image, "width", cute_png_Image__width, NULL);
py_bindproperty(tp_image, "height", cute_png_Image__height, NULL);
@ -196,6 +235,7 @@ void pk__add_module_cute_png() {
py_bindmethod(tp_image, "getpixel", cute_png_Image__getpixel);
py_bindmethod(tp_image, "clear", cute_png_Image__clear);
py_bindmethod(tp_image, "to_rgb565_bytes", cute_png_Image__to_rgb565_bytes);
py_bindmethod(tp_image, "to_png_bytes", cute_png_Image__to_png_bytes);
py_bindmethod(tp_image, "to_png_file", cute_png_Image__to_png_file);
py_bindmethod(tp_image, "to_rgb565_file", cute_png_Image__to_rgb565_file);
}

View File

@ -2,6 +2,7 @@
#include "pocketpy/common/vector.h"
#include "pocketpy/objects/base.h"
#include <stdio.h>
typedef struct {
uint64_t hash;
@ -22,3 +23,9 @@ typedef c11_vector List;
void c11_chunked_array2d__mark(void* ud, c11_vector* p_stack);
void function__gc_mark(void* ud, c11_vector* p_stack);
typedef struct {
FILE* file; // cute_png will cast the whole userdata to FILE**
const char* path;
const char* mode;
} io_FileIO;

View File

@ -11,13 +11,17 @@ class Image:
@staticmethod
def from_bytes(data: bytes) -> "Image": ...
@staticmethod
def from_file(path: str) -> "Image": ...
def setpixel(self, x: int, y: int, color: color32) -> None: ...
def getpixel(self, x: int, y: int) -> color32: ...
def clear(self, color: color32) -> None: ...
def to_rgb565_bytes(self) -> bytes: ...
def to_png_bytes(self) -> bytes: ...
def to_png_file(self, path: str) -> int: ...
def to_rgb565_file(self, path: str) -> int: ...
def loads(data: bytes) -> array2d[color32]: ...
def dumps(image: array2d[color32]) -> bytes: ...

17
include/typings/io.pyi Normal file
View File

@ -0,0 +1,17 @@
class FileIO:
def __new__(cls, file: str, mode: str) -> "FileIO": ...
def __enter__(self) -> "FileIO": ...
def __exit__(self) -> None: ...
def read(self, size: int | None = None) -> bytes: ...
def write(self, data: bytes) -> int: ...
def close(self) -> None: ...
def tell(self) -> int: ...
def seek(self, offset: int, whence: int) -> None: ...
def flush(self) -> None: ...
SEEK_SET: int
SEEK_CUR: int
SEEK_END: int

View File

@ -580,6 +580,7 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// [cls, NULL, args..., kwargs...]
py_Ref new_f = py_tpfindmagic(py_totype(p0), __new__);
assert(new_f && py_isnil(p0 + 1));
bool is_default_new = new_f->type == tp_nativefunc && new_f->_cfunc == pk__object_new;
// prepare a copy of args and kwargs
int span = self->stack.sp - argv;
@ -603,6 +604,13 @@ FrameResult VM__vectorcall(VM* self, uint16_t argc, uint16_t kwargc, bool opcall
// [__init__, self, args..., kwargs...]
if(VM__vectorcall(self, argc, kwargc, false) == RES_ERROR) return RES_ERROR;
*py_retval() = p0[1]; // restore the new instance
} else {
if(is_default_new) {
if(argc != 0 || kwargc != 0) {
TypeError("%t() takes no arguments", py_totype(p0));
return RES_ERROR;
}
}
}
// reset the stack
self->stack.sp = p0;

View File

@ -1,5 +1,6 @@
#include "pocketpy/objects/base.h"
#include "pocketpy/pocketpy.h"
#include "pocketpy/interpreter/types.h"
#include "pocketpy/interpreter/vm.h"
#if PK_ENABLE_OS
@ -101,12 +102,6 @@ void pk__add_module_os() {
py_newdict(py_emplacedict(mod, py_name("environ")));
}
typedef struct {
const char* path;
const char* mode;
FILE* file;
} io_FileIO;
static bool io_FileIO__new__(int argc, py_Ref argv) {
// __new__(cls, file, mode)
PY_CHECK_ARGC(3);
@ -213,6 +208,14 @@ static bool io_FileIO_write(int argc, py_Ref argv) {
return true;
}
static bool io_FileIO_flush(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
io_FileIO* ud = py_touserdata(py_arg(0));
fflush(ud->file);
py_newnone(py_retval());
return true;
}
void pk__add_module_io() {
py_Ref mod = py_newmodule("io");
@ -226,6 +229,7 @@ void pk__add_module_io() {
py_bindmethod(FileIO, "close", io_FileIO_close);
py_bindmethod(FileIO, "tell", io_FileIO_tell);
py_bindmethod(FileIO, "seek", io_FileIO_seek);
py_bindmethod(FileIO, "flush", io_FileIO_flush);
py_newint(py_emplacedict(mod, py_name("SEEK_SET")), SEEK_SET);
py_newint(py_emplacedict(mod, py_name("SEEK_CUR")), SEEK_CUR);

View File

@ -167,8 +167,26 @@ static bool list__setitem__(int argc, py_Ref argv) {
static bool list__delitem__(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_int);
List* self = py_touserdata(py_arg(0));
if(py_istype(py_arg(1), tp_slice)) {
int start, stop, step;
bool ok = pk__parse_int_slice(py_arg(1), self->length, &start, &stop, &step);
if(!ok) return false;
if(step != 1) return ValueError("slice step must be 1 for deletion");
int n = stop - start;
if(n > 0) {
py_TValue* p = self->data;
for(int i = stop; i < self->length; i++) {
p[start + i - stop] = p[i];
}
self->length -= n;
}
py_newnone(py_retval());
return true;
}
PY_CHECK_ARG_TYPE(1, tp_int);
int index = py_toint(py_arg(1));
if(!pk__normalize_index(&index, self->length)) return false;
c11_vector__erase(py_TValue, self, index);

View File

@ -160,6 +160,16 @@ b = [(1, 2), (3, 3), (5, 1)]
b.sort(key=lambda x:x[1])
assert b == [(5, 1), (1, 2), (3,3)]
# test del slice
a = [1, 2, 3, 4]
b = a.copy(); del b[:2]; assert b == [3, 4]
b = a.copy(); del b[1:3]; assert b == [1, 4]
b = a.copy(); del b[2:]; assert b == [1, 2]
b = a.copy(); del b[:-1]; assert b == [4]
b = a.copy(); del b[-1:]; assert b == [1, 2, 3]
b = a.copy(); del b[:]; assert b == []
assert a == [1, 2, 3, 4]
# test cyclic reference
# a = []
# a.append(0)

View File

@ -136,3 +136,23 @@ assert MyClass.c == 2
assert MyClass.d == 3
assert MyClass(1, 2).m == 1
class E: pass
try:
E(1,2,3)
exit(1)
except TypeError:
pass
class E1:
def __new__(cls, a, b):
o = object.__new__(cls)
o.a = a
o.b = b
return o
def sum(self):
return self.a + self.b
e1 = E1(3,4)
assert e1.sum() == 7