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