Implementing a Module

This chapter details how to implement a core module in MicroPython. MicroPython modules can be one of the following:

  • Built-in module: A general module that should be part of the MicroPython repository
  • User module: A module that is useful for your specific project (i.e. project-specific functionality that you maintain in your own repository or private codebase).
  • Dynamic module: A module that can be deployed at runtime to your device

A module in MicroPython can be implemented in one of the following locations:

  • /py: A core library that mirrors core CPython functionality.
  • /extmod: Micropython-specific (or optional core CPython) but shared across multiple ports.
  • /ports/port: port-specific module.

Note

This chapter describes modules implemented in /py or core modules . See Extending MicroPython in C for details on implementing an external module. For details on port-specific modules, see Porting MicroPython.

Implementing a core module

Like CPython, MicroPython has core builtin modules that can be accessed through import statements. An example is the gc module discussed in Memory Management.

>>> import gc
>>> gc.enable()
>>>

MicroPython has several other builtin standard/core modules like io, uarray etc. Adding a new core module involves several modifications.

First, create the C file in the py directory. In this example, we are adding a new module subinterpreters in the file modsubinterpreters.c:

#include "py/builtin.h"
#include "py/gc.h"

#if MICROPY_PY_SUBINTERPRETERS

// list()
STATIC mp_obj_t py_subinterpreters_list(void) {
     gc_collect();
     #if MICROPY_PY_GC_COLLECT_RETVAL
         return MP_OBJ_NEW_SMALL_INT(MP_STATE_MEM(gc_collected));
     #else
         return mp_const_none;
     #endif
}
MP_DEFINE_CONST_FUN_OBJ_0(subinterpreters_list_obj, py_subinterpreters_list);

STATIC const mp_rom_map_elem_t mp_module_subinterpreters_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_subinterpreters) },
     { MP_ROM_QSTR(MP_QSTR_list), MP_ROM_PTR(&subinterpreters_list_obj) },
};

STATIC MP_DEFINE_CONST_DICT(mp_module_subinterpreters_globals, mp_module_subinterpreters_globals_table);

const mp_obj_module_t mp_module_subinterpreters = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&mp_module_subinterpreters_globals,
};

#endif

The implementation includes a definition of all functions related to the module and adds the functions to the module’s global table. It also registers the module with its table of globals:

const mp_obj_module_t mp_module_subinterpreters = {
 .base = { &mp_type_module },
 .globals = (mp_obj_dict_t *)&mp_module_subinterpreters_globals,
};

Expose the module for use in Python with:

MP_REGISTER_MODULE(MP_QSTR_subinterpreters, mp_module_subinterpreters, MICROPY_PY_SUBINTERPRETERS);

After the above implementation, expose the module in the builtins header file by modifying the builtins.h file:

extern const mp_obj_module_t mp_module_subinterpreters;

Then modify objmodule.c with the module details:

#if MICROPY_PY_SUBINTERPRETERS
 { MP_ROM_QSTR(MP_QSTR_subinterpreters), MP_ROM_PTR(&mp_module_subinterpreters) },
#endif

If this was a success, the module should now be importable:

>>> import subinterpreters
>>> subinterpreters.list()
>>>

Our list() function currently returns nothing as it calls gc_collect().