Search code examples
pythonimportpython-modulemicropythonpython-importlib

In MicroPython how do I load a module from a string


In a ESP32/MicroPython based project I want to (re)load modules from RAM without having to write them to the flash-based filesystem first. (this is both time consuming and wearing off the flash memory)

So my idea was to retrieve e.g. module.py via web and then turn it into an actual module by using __import__, exec() and so on. But I don't know how.

Actually what I need is quite similar to this: How to load a module from code in a string?

In MicroPython there are no imp or importlib or even types.ModuleType module but at least you have __import__.

Is there a way to implement

my_code = 'a = 5'
mymodule = imp.new_module('mymodule')
exec(my_code, mymodule.__dict__)

Without imp.new_module?

I tried sys.__class__('mymodule') in order smuggle the module type out of an existing module but I get

>>> mymodule = sys.__class__('mymodule')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't create 'module' instances

Solution

  • I don't know if this is feasible for you, but an alternative approach might be to create a filesystem in ram, copy the string to that, then use conventional import statements.

    The MicroPython reference says "filesystems can also use external flash, RAM,...", and "MicroPython implements a Unix-like Virtual File System (VFS) layer", and goes on to provide the code to implement a simple block device in RAM:

    class RAMBlockDev:
        def __init__(self, block_size, num_blocks):
            self.block_size = block_size
            self.data = bytearray(block_size * num_blocks)
    
        def readblocks(self, block_num, buf):
            for i in range(len(buf)):
                buf[i] = self.data[block_num * self.block_size + i]
    
        def writeblocks(self, block_num, buf):
            for i in range(len(buf)):
                self.data[block_num * self.block_size + i] = buf[i]
    
        def ioctl(self, op, arg):
            if op == 4: # get number of blocks
                return len(self.data) // self.block_size
            if op == 5: # get block size
                return self.block_size
    

    You create and mount a filesystem simply:

    import os
    bdev = RAMBlockDev(512, 50)
    os.VfsFat.mkfs(bdev)
    os.mount(bdev, '/ramdisk')
    

    and then use it simply:

    with open('/ramdisk/hello.txt', 'w') as f:
        f.write('Hello world')
    print(open('/ramdisk/hello.txt').read())