mirror of
https://github.com/pocketpy/pocketpy
synced 2026-08-12 17:17:11 +08:00
Compare commits
3 Commits
7d19f78ef5
...
14d1572328
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14d1572328 | ||
|
|
acc4f4104b | ||
|
|
46cc281099 |
@ -78,6 +78,13 @@ typedef struct py_Callbacks {
|
|||||||
PY_MAYBENULL void (*gc_mark)(void (*f)(py_Ref val, void* ctx), void* ctx);
|
PY_MAYBENULL void (*gc_mark)(void (*f)(py_Ref val, void* ctx), void* ctx);
|
||||||
/// Used by `PRINT_EXPR` bytecode.
|
/// Used by `PRINT_EXPR` bytecode.
|
||||||
PY_MAYBENULL bool (*displayhook)(py_Ref val) PY_RAISE;
|
PY_MAYBENULL bool (*displayhook)(py_Ref val) PY_RAISE;
|
||||||
|
// open_file hook contributed by fdtd.io (Hector), 2026.
|
||||||
|
/// Consulted before a script-reachable file operation. `path` is the target path;
|
||||||
|
/// `mode` is the fopen mode string for `io.FileIO`, or the literal "delete" for
|
||||||
|
/// `os.remove`. Return true to allow the operation, false to reject it (the binding
|
||||||
|
/// then raises OSError). NULL (the default) allows everything, so existing embedders
|
||||||
|
/// are unaffected. Lets an embedder enforce its own path policy.
|
||||||
|
PY_MAYBENULL bool (*open_file)(const char* path, const char* mode);
|
||||||
} py_Callbacks;
|
} py_Callbacks;
|
||||||
|
|
||||||
/// A struct contains the application-level callbacks.
|
/// A struct contains the application-level callbacks.
|
||||||
|
|||||||
@ -124,6 +124,20 @@ static bool type__annotations__(int argc, py_Ref argv) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool type__subclasses__(int argc, py_Ref argv) {
|
||||||
|
PY_CHECK_ARGC(1);
|
||||||
|
py_TypeInfo* base_ti = py_touserdata(argv);
|
||||||
|
py_newlist(py_retval());
|
||||||
|
|
||||||
|
for(py_Type i = 1; i < pk_current_vm->types.length; i++) {
|
||||||
|
py_TypeInfo* ti = pk_typeinfo(i);
|
||||||
|
if(ti->base == base_ti->index) {
|
||||||
|
py_list_append(py_retval(), &ti->self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void pk_object__register() {
|
void pk_object__register() {
|
||||||
py_bindmagic(tp_object, __new__, pk__object_new);
|
py_bindmagic(tp_object, __new__, pk__object_new);
|
||||||
|
|
||||||
@ -142,4 +156,5 @@ void pk_object__register() {
|
|||||||
py_bindproperty(tp_type, "__name__", type__name__, NULL);
|
py_bindproperty(tp_type, "__name__", type__name__, NULL);
|
||||||
py_bindproperty(tp_object, "__dict__", object__dict__, NULL);
|
py_bindproperty(tp_object, "__dict__", object__dict__, NULL);
|
||||||
py_bindproperty(tp_type, "__annotations__", type__annotations__, NULL);
|
py_bindproperty(tp_type, "__annotations__", type__annotations__, NULL);
|
||||||
|
py_bindmethod(tp_type, "__subclasses__", type__subclasses__);
|
||||||
}
|
}
|
||||||
@ -92,6 +92,7 @@ void VM__ctor(VM* self) {
|
|||||||
self->callbacks.print = pk_default_print;
|
self->callbacks.print = pk_default_print;
|
||||||
self->callbacks.flush = pk_default_flush;
|
self->callbacks.flush = pk_default_flush;
|
||||||
self->callbacks.getchr = pk_default_getchr;
|
self->callbacks.getchr = pk_default_getchr;
|
||||||
|
self->callbacks.open_file = NULL;
|
||||||
|
|
||||||
self->last_retval = *py_NIL();
|
self->last_retval = *py_NIL();
|
||||||
self->unhandled_exc = *py_NIL();
|
self->unhandled_exc = *py_NIL();
|
||||||
|
|||||||
@ -71,6 +71,10 @@ static bool os_remove(int argc, py_Ref argv) {
|
|||||||
PY_CHECK_ARGC(1);
|
PY_CHECK_ARGC(1);
|
||||||
PY_CHECK_ARG_TYPE(0, tp_str);
|
PY_CHECK_ARG_TYPE(0, tp_str);
|
||||||
const char* path = py_tostr(py_arg(0));
|
const char* path = py_tostr(py_arg(0));
|
||||||
|
// open_file policy hook: "delete" pseudo-mode for os.remove.
|
||||||
|
if(pk_current_vm->callbacks.open_file && !pk_current_vm->callbacks.open_file(path, "delete")) {
|
||||||
|
return OSError("os.remove not permitted: '%s'", path);
|
||||||
|
}
|
||||||
int code = remove(path);
|
int code = remove(path);
|
||||||
if(code != 0) {
|
if(code != 0) {
|
||||||
const char* msg = strerror(errno);
|
const char* msg = strerror(errno);
|
||||||
@ -111,6 +115,11 @@ static bool io_FileIO__new__(int argc, py_Ref argv) {
|
|||||||
io_FileIO* ud = py_newobject(py_retval(), cls, 0, sizeof(io_FileIO));
|
io_FileIO* ud = py_newobject(py_retval(), cls, 0, sizeof(io_FileIO));
|
||||||
ud->path = py_tostr(py_arg(1));
|
ud->path = py_tostr(py_arg(1));
|
||||||
ud->mode = py_tostr(py_arg(2));
|
ud->mode = py_tostr(py_arg(2));
|
||||||
|
// open_file policy hook: consulted with the fopen mode string before the open.
|
||||||
|
if(pk_current_vm->callbacks.open_file &&
|
||||||
|
!pk_current_vm->callbacks.open_file(ud->path, ud->mode)) {
|
||||||
|
return OSError("file open not permitted: '%s' (mode '%s')", ud->path, ud->mode);
|
||||||
|
}
|
||||||
ud->file = fopen(ud->path, ud->mode);
|
ud->file = fopen(ud->path, ud->mode);
|
||||||
if(ud->file == NULL) {
|
if(ud->file == NULL) {
|
||||||
const char* msg = strerror(errno);
|
const char* msg = strerror(errno);
|
||||||
|
|||||||
@ -50,3 +50,21 @@ assert hasattr(a, 'zzz')
|
|||||||
|
|
||||||
assert not hasattr(a, '')
|
assert not hasattr(a, '')
|
||||||
|
|
||||||
|
class Base:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Child1(Base):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Child2(Base):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class GrandChild(Child1):
|
||||||
|
pass
|
||||||
|
|
||||||
|
subs = Base.__subclasses__()
|
||||||
|
assert type(subs) is list
|
||||||
|
assert Child1 in subs
|
||||||
|
assert Child2 in subs
|
||||||
|
assert GrandChild not in subs
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user