[Solved] User Extension Classes

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
hippy
Posts: 130
Joined: Sat Feb 20, 2021 2:46 pm
Location: UK

[Solved] User Extension Classes

Post by hippy » Tue Oct 19, 2021 5:49 pm

I have a C Extension Module 'database' which implements a 'lookup' user type, which implements a 'phone_number' method. That allows -

Code: Select all

>>> database.lookup().phone_number("Operator")
'100'
But how would I need to implement that such that I can drop the parenthesis after lookup, would allow -

Code: Select all

>>> database.lookup.phone_number("Operator")
Rather than return an object which is a user type, I am guessing I need to return something which is an actual class.

I could not see anything in existing MicroPython C Modules which do that themselves, and Googling did not help. I might be looking in the wrong place, searching for the wrong thing, using the wrong terminology, so any pointers would be appreciated.

hippy
Posts: 130
Joined: Sat Feb 20, 2021 2:46 pm
Location: UK

Re: [Solved] User Extension Classes

Post by hippy » Tue Oct 19, 2021 9:38 pm

hippy wrote:
Tue Oct 19, 2021 5:49 pm
Rather than return an object which is a user type, I am guessing I need to return something which is an actual class.
Or a module.

Keeping up the tradition of just posting about a problem leading to a break-through - This works for me -

Code: Select all

#include "py/runtime.h"
#include "py/obj.h"
#include "py/objstr.h"

// lookup sub-module

STATIC mp_obj_t lookup_phone_number(mp_obj_t name_obj) {
    const char *name = mp_obj_str_get_str(name_obj);
    (void)name;  // Usual code replaced here
    return MP_OBJ_NEW_SMALL_INT(100);
}
MP_DEFINE_CONST_FUN_OBJ_1(lookup_phone_number_obj, lookup_phone_number);

STATIC const mp_rom_map_elem_t lookup_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR_phone_number), MP_ROM_PTR(&lookup_phone_number_obj) },
};
STATIC MP_DEFINE_CONST_DICT(lookup_module_globals,
                            lookup_module_globals_table);

STATIC const mp_obj_module_t module_lookup = {
    .base = { &mp_type_module },
    .globals = (mp_obj_dict_t *)&lookup_module_globals,
};

// database module

STATIC const mp_rom_map_elem_t database_module_globals_table[] = {
    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_database) },
    { MP_ROM_QSTR(MP_QSTR_lookup),   MP_ROM_PTR(&module_lookup) },
};
STATIC MP_DEFINE_CONST_DICT(database_module_globals,
                            database_module_globals_table);

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

MP_REGISTER_MODULE(MP_QSTR_database, module_database, 1);

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: [Solved] User Extension Classes

Post by dhylands » Tue Oct 19, 2021 10:34 pm

In order to lose the parenthesis, lookup needs to resolve to a dictionary rather than a function.

As well as defining attributes statically (with the QSTR/obj pointer) you can implement a .attr function which will do the lookup at runtime.
IIRC the struct/uctypes classes do a dynamic lookup via the .attr function.

For an example of how this is done statically, take a look at the pins. Something like machine.Pin.cpu.A3
pin.c has the Pin module. The board and cpu attributes are pointers to dictionaries: https://github.com/micropython/micropyt ... #L548-L549
which you can see defined here:
https://github.com/micropython/micropyt ... .c#L34-L44

The actual contents of those dictionaries are generated, so you need to look in the build directory. For the PYBV11 board, you'd look at
ports/stm32/build-PYBV11/pins_PYBV11.c file and you'll see that pin_cpu_pins_locals_dict_table looks like:

Code: Select all

STATIC const mp_rom_map_elem_t pin_cpu_pins_locals_dict_table[] = {
  { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_A0_obj) },
  { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_A1_obj) },
  { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_A2_obj) },
  { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_A3_obj) },
  { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_A4_obj) },
...
  { MP_ROM_QSTR(MP_QSTR_C13), MP_ROM_PTR(&pin_C13_obj) },
  { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_D2_obj) },
};
MP_DEFINE_CONST_DICT(pin_cpu_pins_locals_dict, pin_cpu_pins_locals_dict_table);

hippy
Posts: 130
Joined: Sat Feb 20, 2021 2:46 pm
Location: UK

Re: [Solved] User Extension Classes

Post by hippy » Tue Oct 19, 2021 10:44 pm

Thanks. I don't think a dictionary would work in this case because the database is loaded at run-time and there is pre-processing done on the input before it actually retrieves a result. That '.attr' looks interesting though; I'll have a read up on that.

Post Reply