From 5e0452125146f3afed89c09f5813790156d24471 Mon Sep 17 00:00:00 2001 From: Jim Mussared Date: Fri, 12 May 2023 17:03:14 +1000 Subject: [PATCH] examples/usercmodule: Add a sub-package example. This demonstrates how to add a sub-package in a user c module, as well as how to define the necessary qstrs and enable the feature in the build. This is used by the unix coverage build to test this feature. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared --- examples/usercmodule/cexample/examplemodule.c | 2 +- examples/usercmodule/cexample/micropython.mk | 7 +- .../usercmodule/cppexample/examplemodule.c | 2 +- examples/usercmodule/subpackage/README.md | 1 + .../usercmodule/subpackage/micropython.cmake | 19 +++++ .../usercmodule/subpackage/micropython.mk | 10 +++ .../subpackage/modexamplepackage.c | 84 +++++++++++++++++++ .../subpackage/qstrdefsexamplepackage.h | 2 + tests/unix/extra_coverage.py | 17 ++++ tests/unix/extra_coverage.py.exp | 25 ++++-- 10 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 examples/usercmodule/subpackage/README.md create mode 100644 examples/usercmodule/subpackage/micropython.cmake create mode 100644 examples/usercmodule/subpackage/micropython.mk create mode 100644 examples/usercmodule/subpackage/modexamplepackage.c create mode 100644 examples/usercmodule/subpackage/qstrdefsexamplepackage.h diff --git a/examples/usercmodule/cexample/examplemodule.c b/examples/usercmodule/cexample/examplemodule.c index ccce03bcbd..9416613ba9 100644 --- a/examples/usercmodule/cexample/examplemodule.c +++ b/examples/usercmodule/cexample/examplemodule.c @@ -68,7 +68,7 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &example_Timer_locals_dict ); -// Define all properties of the module. +// Define all attributes of the module. // Table entries are key/value pairs of the attribute name (a string) // and the MicroPython object reference. // All identifiers and strings are written as MP_QSTR_xxx and will be diff --git a/examples/usercmodule/cexample/micropython.mk b/examples/usercmodule/cexample/micropython.mk index dbfe3c5cbd..d6801dac0b 100644 --- a/examples/usercmodule/cexample/micropython.mk +++ b/examples/usercmodule/cexample/micropython.mk @@ -1,9 +1,8 @@ -EXAMPLE_MOD_DIR := $(USERMOD_DIR) +CEXAMPLE_MOD_DIR := $(USERMOD_DIR) # Add all C files to SRC_USERMOD. -SRC_USERMOD += $(EXAMPLE_MOD_DIR)/examplemodule.c +SRC_USERMOD += $(CEXAMPLE_MOD_DIR)/examplemodule.c # We can add our module folder to include paths if needed # This is not actually needed in this example. -CFLAGS_USERMOD += -I$(EXAMPLE_MOD_DIR) -CEXAMPLE_MOD_DIR := $(USERMOD_DIR) +CFLAGS_USERMOD += -I$(CEXAMPLE_MOD_DIR) diff --git a/examples/usercmodule/cppexample/examplemodule.c b/examples/usercmodule/cppexample/examplemodule.c index 5c84eccd79..96a1a74438 100644 --- a/examples/usercmodule/cppexample/examplemodule.c +++ b/examples/usercmodule/cppexample/examplemodule.c @@ -4,7 +4,7 @@ // See example.cpp for the definition. STATIC MP_DEFINE_CONST_FUN_OBJ_2(cppfunc_obj, cppfunc); -// Define all properties of the module. +// Define all attributes of the module. // Table entries are key/value pairs of the attribute name (a string) // and the MicroPython object reference. // All identifiers and strings are written as MP_QSTR_xxx and will be diff --git a/examples/usercmodule/subpackage/README.md b/examples/usercmodule/subpackage/README.md new file mode 100644 index 0000000000..c7f2ee53a2 --- /dev/null +++ b/examples/usercmodule/subpackage/README.md @@ -0,0 +1 @@ +This is an example of a user C module that includes subpackages. diff --git a/examples/usercmodule/subpackage/micropython.cmake b/examples/usercmodule/subpackage/micropython.cmake new file mode 100644 index 0000000000..a51e7a8061 --- /dev/null +++ b/examples/usercmodule/subpackage/micropython.cmake @@ -0,0 +1,19 @@ +# Create an INTERFACE library for our C module. +add_library(usermod_subpackage_example INTERFACE) + +# Add our source files to the lib +target_sources(usermod_subpackage_example INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/examplemodule.c +) + +# Add the current directory as an include directory. +target_include_directories(usermod_subpackage_example INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_subpackage_example INTERFACE + MICROPY_MODULE_BUILTIN_SUBPACKAGES=1 +) + +# Link our INTERFACE library to the usermod target. +target_link_libraries(usermod INTERFACE usermod_subpackage_example) diff --git a/examples/usercmodule/subpackage/micropython.mk b/examples/usercmodule/subpackage/micropython.mk new file mode 100644 index 0000000000..99ebf13ec1 --- /dev/null +++ b/examples/usercmodule/subpackage/micropython.mk @@ -0,0 +1,10 @@ +SUBPACKAGE_EXAMPLE_MOD_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD += $(SUBPACKAGE_EXAMPLE_MOD_DIR)/modexamplepackage.c + +# We can add our module folder to include paths if needed +# This is not actually needed in this example. +CFLAGS_USERMOD += -I$(SUBPACKAGE_EXAMPLE_MOD_DIR) -DMICROPY_MODULE_BUILTIN_SUBPACKAGES=1 + +QSTR_DEFS += $(SUBPACKAGE_EXAMPLE_MOD_DIR)/qstrdefsexamplepackage.h diff --git a/examples/usercmodule/subpackage/modexamplepackage.c b/examples/usercmodule/subpackage/modexamplepackage.c new file mode 100644 index 0000000000..70e1e4005b --- /dev/null +++ b/examples/usercmodule/subpackage/modexamplepackage.c @@ -0,0 +1,84 @@ +// Include MicroPython API. +#include "py/runtime.h" + +// Define example_package.foo.bar.f() +STATIC mp_obj_t example_package_foo_bar_f(void) { + mp_printf(&mp_plat_print, "example_package.foo.bar.f\n"); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package_foo_bar_f_obj, example_package_foo_bar_f); + +// Define all attributes of the second-level sub-package (example_package.foo.bar). +STATIC const mp_rom_map_elem_t example_package_foo_bar_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example_package_dot_foo_dot_bar) }, + { MP_ROM_QSTR(MP_QSTR_f), MP_ROM_PTR(&example_package_foo_bar_f_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(example_package_foo_bar_globals, example_package_foo_bar_globals_table); + +// Define example_package.foo.bar module object. +const mp_obj_module_t example_package_foo_bar_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&example_package_foo_bar_globals, +}; + +// Define example_package.foo.f() +STATIC mp_obj_t example_package_foo_f(void) { + mp_printf(&mp_plat_print, "example_package.foo.f\n"); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package_foo_f_obj, example_package_foo_f); + +// Define all attributes of the first-level sub-package (example_package.foo). +STATIC const mp_rom_map_elem_t example_package_foo_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example_package_dot_foo) }, + { MP_ROM_QSTR(MP_QSTR_bar), MP_ROM_PTR(&example_package_foo_bar_user_cmodule) }, + { MP_ROM_QSTR(MP_QSTR_f), MP_ROM_PTR(&example_package_foo_f_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(example_package_foo_globals, example_package_foo_globals_table); + +// Define example_package.foo module object. +const mp_obj_module_t example_package_foo_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&example_package_foo_globals, +}; + +// Define example_package.f() +STATIC mp_obj_t example_package_f(void) { + mp_printf(&mp_plat_print, "example_package.f\n"); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package_f_obj, example_package_f); + +STATIC mp_obj_t example_package___init__(void) { + if (!MP_STATE_VM(example_package_initialised)) { + // __init__ for builtins is called each time the module is imported, + // so ensure that initialisation only happens once. + MP_STATE_VM(example_package_initialised) = true; + mp_printf(&mp_plat_print, "example_package.__init__\n"); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(example_package___init___obj, example_package___init__); + +// The "initialised" state is stored on mp_state so that it is cleared on soft +// reset. +MP_REGISTER_ROOT_POINTER(int example_package_initialised); + +// Define all attributes of the top-level package (example_package). +STATIC const mp_rom_map_elem_t example_package_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example_package) }, + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&example_package___init___obj) }, + { MP_ROM_QSTR(MP_QSTR_foo), MP_ROM_PTR(&example_package_foo_user_cmodule) }, + { MP_ROM_QSTR(MP_QSTR_f), MP_ROM_PTR(&example_package_f_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(example_package_globals, example_package_globals_table); + +// Define module object. +const mp_obj_module_t example_package_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&example_package_globals, +}; + +// Register the module to make it available in Python. +// Note: subpackages should not be registered with MP_REGISTER_MODULE. +MP_REGISTER_MODULE(MP_QSTR_example_package, example_package_user_cmodule); diff --git a/examples/usercmodule/subpackage/qstrdefsexamplepackage.h b/examples/usercmodule/subpackage/qstrdefsexamplepackage.h new file mode 100644 index 0000000000..057ec5279d --- /dev/null +++ b/examples/usercmodule/subpackage/qstrdefsexamplepackage.h @@ -0,0 +1,2 @@ +Q(example_package.foo) +Q(example_package.foo.bar) diff --git a/tests/unix/extra_coverage.py b/tests/unix/extra_coverage.py index a51b5b856a..3c12b26923 100644 --- a/tests/unix/extra_coverage.py +++ b/tests/unix/extra_coverage.py @@ -96,3 +96,20 @@ print(returns_NULL()) import frozentest print(frozentest.__file__) + +# test for builtin sub-packages +from example_package.foo import bar + +print(bar) +bar.f() +import example_package + +print(example_package, example_package.foo, example_package.foo.bar) +example_package.f() +example_package.foo.f() +example_package.foo.bar.f() +print(bar == example_package.foo.bar) +from example_package.foo import f as foo_f + +foo_f() +print(foo_f == example_package.foo.f) diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index ab6d497723..7ea2599c91 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -52,13 +52,14 @@ mport builtins micropython _thread _uasyncio btree cexample cmath cppexample -ffi framebuf gc math -termios uarray ubinascii ucollections -ucryptolib uctypes uerrno uhashlib -uheapq uio ujson umachine -uos urandom ure uselect -usocket ussl ustruct usys -utime utimeq uwebsocket uzlib +example_package ffi framebuf +gc math termios uarray +ubinascii ucollections ucryptolib uctypes +uerrno uhashlib uheapq uio +ujson umachine uos urandom +ure uselect usocket ussl +ustruct usys utime utimeq +uwebsocket uzlib ime utime utimeq @@ -211,3 +212,13 @@ b'bytes 1234\x01' 2 3 frozentest.py +example_package.__init__ + +example_package.foo.bar.f + +example_package.f +example_package.foo.f +example_package.foo.bar.f +True +example_package.foo.f +True