Berry add `introspect.setmodule(name:string, value:any) -> nil`

This commit is contained in:
Stephan Hadinger 2022-09-25 15:33:31 +02:00
parent 28a1859b01
commit 3f5baaa0c6
5 changed files with 84 additions and 6 deletions

View File

@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file.
- Zigbee support for decimal Voltage/Current/Power on power metering plugs
- Command ``UrlFetch <url>`` to download a file to filesystem
- Zigbee basic support for Green Power
- Berry add ``introspect.setmodule(name:string, value:any) -> nil``
### Changed
- ESP32 Increase number of button GPIOs from 8 to 28 (#16518)

View File

@ -66,10 +66,25 @@ static void m_findmember_protected(bvm *vm, void* data)
static int m_findmember(bvm *vm)
{
int top = be_top(vm);
if (top >= 2 && (be_isinstance(vm, 1) || be_ismodule(vm, 1) || be_isclass(vm, 1)) && be_isstring(vm, 2)) {
int ret = be_execprotected(vm, &m_findmember_protected, (void*) be_tostring(vm, 2));
if (ret == BE_OK) {
be_return(vm);
bbool protected = btrue; /* run protected, i.e. don't raise an exception if not found */
if (top >= 3) {
protected = !be_tobool(vm, 3);
}
if (top >= 2) {
if (protected && (be_isinstance(vm, 1) || be_ismodule(vm, 1) || be_isclass(vm, 1)) && be_isstring(vm, 2)) {
int ret = be_execprotected(vm, &m_findmember_protected, (void*) be_tostring(vm, 2));
if (ret == BE_OK) {
be_return(vm);
}
} else {
/* run unprotected */
if (be_getmember(vm, 1, be_tostring(vm, 2))) {
be_return(vm);
} else {
/* not found, return module 'undefined' */
be_getmodule(vm, "undefined");
be_return(vm);
}
}
}
be_return_nil(vm);
@ -143,6 +158,20 @@ static int m_getmodule(bvm *vm)
be_return_nil(vm);
}
/* set or chang the cached value for the named module, this allows monkey patching. **USE WITH CARE** */
static int m_setmodule(bvm *vm)
{
int top = be_top(vm);
if (top >= 2) {
bvalue *v = be_indexof(vm, 1);
if (var_isstr(v)) {
be_pushvalue(vm, 2); /* ensure the second arg is at top of stack */
be_cache_module(vm, var_tostr(v));
}
}
be_return_nil(vm);
}
/* checks if the function (berry bytecode bproto only) is hinted as a method */
static int m_ismethod(bvm *vm)
{
@ -167,6 +196,7 @@ be_native_module_attr_table(introspect) {
be_native_module_function("set", m_setmember),
be_native_module_function("module", m_getmodule),
be_native_module_function("setmodule", m_setmodule),
be_native_module_function("toptr", m_toptr),
be_native_module_function("fromptr", m_fromptr),
@ -184,6 +214,7 @@ module introspect (scope: global, depend: BE_USE_INTROSPECT_MODULE) {
set, func(m_setmember)
module, func(m_getmodule)
setmodule, func(m_setmodule)
toptr, func(m_toptr)
fromptr, func(m_fromptr)

View File

@ -245,7 +245,7 @@ static bvalue* load_cached(bvm *vm, bstring *path)
return v;
}
static void cache_module(bvm *vm, bstring *name)
void be_cache_module(bvm *vm, bstring *name)
{
bvalue *v;
if (vm->module.loaded == NULL) {
@ -282,7 +282,7 @@ int be_module_load(bvm *vm, bstring *path)
if (res == BE_OK) {
/* on first load of the module, try running the '()' function */
module_init(vm);
cache_module(vm, path);
be_cache_module(vm, path);
}
}
return res;

View File

@ -34,6 +34,7 @@ typedef struct bmodule {
bmodule* be_module_new(bvm *vm);
void be_module_delete(bvm *vm, bmodule *module);
int be_module_load(bvm *vm, bstring *path);
void be_cache_module(bvm *vm, bstring *name);
int be_module_attr(bvm *vm, bmodule *module, bstring *attr, bvalue *dst);
bbool be_module_setmember(bvm *vm, bmodule *module, bstring *attr, bvalue *src);
const char* be_module_name(bmodule *module);

View File

@ -0,0 +1,45 @@
# test for monkey patching of modules
import string
import introspect
var string_orig = string
introspect.setmodule("string", 42)
import string
assert(string == 42)
# set back original value
introspect.setmodule("string", string_orig)
import string
assert(type(string) == 'module')
assert(str(string) == '<module: string>')
#
# demo, how to monkey patch string with a new function `foo()` returning `bar`
#
import string
import introspect
var string_orig = string # keep a copy of the original string module
var string_alt = module('string') # create a new module
# function `super()` is a closure to capture the original value of string
string_alt.super = def()
return string_orig
end
# function `member()` is a clousre to capture the original value of string module
string_alt.member = def(k)
import introspect
return introspect.get(string_orig, k, true)
end
string_alt.foo = def() return 'bar' end
# replace the entry for module "string", from now on each `import string` will use `string_alt`
introspect.setmodule("string", string_alt)
import string
# test the new string module
assert(string.tolower('abCD') == 'abcd')
assert(string.foo() == 'bar')