2

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
frans
  • 8,868
  • 11
  • 58
  • 132
  • If all you need is the namespace/`.__dict__`, then why can't you just use a dummy class instead of `ModuleType`? – Will Chen Nov 13 '21 at 20:21

1 Answers1

2

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())
meuh
  • 11,500
  • 2
  • 29
  • 45
  • this is actually what I'm doing right now - I was hoping there is some nice way to travel light, but it looks like this is the way to conserve your flash memory. I'm wondering why MicroPython consistently removes every way to create a module from scratch.. Anyway I hope this is not _the_ final answer but I'm giving your +1 because it works. And in case there is no nice way I'll make this the answer (I'll have to re-word my question first :)) – frans Sep 13 '21 at 16:52