Uruchamianie kodu Pythona w piaskownicy za pomocą MicroPython i WASM
Uruchamianie kodu Pythona w piaskownicy za pomocą MicroPython i WASM
6 czerwca 2026 r
Od kilku lat eksperymentuję z różnymi podejściami do uruchamiania kodu w piaskownicy, ale moja ostatnia próba wydaje mi się, że w końcu może mieć wszystkie cechy, których szukałem. Wydałem go jako pakiet alfa o nazwie micropython-wasm i używam go do wtyczki piaskownicy do wykonywania kodu dla agenta Datasette o nazwie datasette-agent-micropython.
Dlaczego chcę piaskownicę?
Wszystkie moje kluczowe projekty open source — Datasette, LLM, a nawet sqlite-utils — obsługują wtyczki.
Bardzo lubię wtyczki jako mechanizm rozbudowy oprogramowania. Starannie zaprojektowany system wtyczek zmniejsza ryzyko związane z próbowaniem nowych rzeczy prawie do zera — nawet najśmielsze pomysły nie pozostawią trwałego wpływu na samą podstawową aplikację. W moim oprogramowaniu można dodać nową funkcję z dnia na dzień i nie muszę nawet przeglądać żądania ściągnięcia!
Jest jedna poważna wada: wszystkie moje systemy wtyczek używają Pythona i Pluggy, a kod wtyczki jest wykonywany z pełnymi uprawnieniami w moich aplikacjach. Błędna lub złośliwa wtyczka może wszystko zepsuć lub spowodować wyciek prywatnych danych.
Bardzo chciałbym móc uruchamiać kod w stylu wtyczki w środowisku, w którym nie można odczytać niezatwierdzonych plików, połączyć się z siecią lub ogólnie działać w sposób ryzykowny lub szkodliwy dla reszty aplikacji lub komputera użytkownika.
Moje zainteresowania obejmują nie tylko wtyczki. Szczególnie w przypadku Datasette jest wiele funkcji, które chciałbym obsługiwać, gdy przydatne byłoby wykonanie dowolnego kodu. Eksperymentowałem już z tym w przypadku wzbogacania zbiorów danych, gdzie można użyć kodu do przekształcenia wartości przechowywanych w tabeli. Bardzo chciałbym zbudować mechanizm, w którym można uruchamiać kod zgodnie z harmonogramem, który pobiera kod JSON z zatwierdzonej lokalizacji, uruchamia niewielki fragment kodu w celu sformatowania go na listę słowników, a następnie wstawia je jako wiersze w tabeli bazy danych SQLite.
Czego chcę od piaskownicy
Moim celem jest bezpieczne wykonywanie kodu w moich własnych aplikacjach w języku Python. Oto czego potrzebuję:
- Zależności to zainstaluj czysto z PyPIw tym w razie potrzeby koła binarne na wielu platformach. Nie chcę, aby osoby korzystające z mojego oprogramowania musiały podejmować jakiekolwiek dodatkowe kroki poza bezpośrednią instalacją mojego pakietu Pythona.
- Wykonywany kod musi podlegać obu pamięć I Procesor limity. nie chcę
while True: s += "longer string"spowodować awarię mojej aplikacji lub komputera użytkownika. - Dostęp do plików musi być ściśle kontrolowany. Albo w ogóle nie mam dostępu do systemu plików, albo mogę dokładnie zdefiniować, które pliki można czytać, a w których można zapisywać.
- Dostęp do sieci jest również kontrolowany. Kod w trybie piaskownicy nie powinien być w stanie komunikować się z niczym bez przejścia przez warstwę, którą w pełni kontroluję.
- Wsparcie interakcji z funkcje gospodarza. Piaskownica nie na wiele się przyda, jeśli nie mogę dokładnie udostępnić wybranych funkcji platformy kodowi, na którym jest ona uruchomiona.
- To musi być solidny, wspierany i jasno udokumentowany. Straciłem rachubę, ile projektów piaskownicy widziałem w repozytoriach z ostrzeżeniami, że nie są one aktywnie utrzymywane!
WebAssembly wygląda tutaj naprawdę obiecująco
Przeglądarki internetowe działają w najbardziej nieprzyjaznym środowisku, jakie można sobie wyobrazić, jeśli chodzi o złośliwy kod. Ich zadaniem jest pobieranie i wykonać niezaufany kod z Internetu przy prawie każdym ładowaniu strony.
Biorąc to pod uwagę, silniki JavaScript powinny być doskonałymi kandydatami na piaskownice. Niestety te silniki są również niezwykle skomplikowane i nie są przeznaczone do łatwego osadzania w innych projektach. Większość projektów v8 w Pythonie, które widziałem, jest rzadko konserwowana i zawiera ostrzeżenia, aby nie używać ich z całkowicie niezaufanym kodem.
WebAssembly to dużo lepiej kandydat. Został zaprojektowany od początku tak, aby obsługiwał wszystkie funkcje, na których mi zależy i był testowany w przeglądarkach przez prawie dekadę. Biblioteka wasmtime Pythona jest aktywnie utrzymywana i ma koła binarne.
MicroPython w WebAssembly
Silniki WebAssembly, takie jak wasmtime, uruchamiają pliki binarne WebAssembly. Niektóre języki programowania, takie jak Rust, można łatwo skompilować bezpośrednio do WebAssembly. Języki dynamiczne, takie jak JavaScript i Python, są trudniejsze — obsługują prymitywne języki, takie jak eval()co oznacza, że potrzebują pełnego interpretera dostępnego w czasie wykonywania.
Aby uruchomić Python, potrzebujemy pełnego interpretera Pythona skompilowanego do WebAssembly, podłączonego w sposób ułatwiający wprowadzenie kodu, podłączenie funkcji hosta i dostęp do wyników.
Pyodide oferuje znakomity pakiet do uruchamiania Pythona przy użyciu WebAssembly w przeglądarce, ale używanie Pyodide w Pythonie po stronie serwera nie jest obsługiwane. Najnowsza rada, jaką udało mi się znaleźć, pochodzi z października 2024 r. i stwierdza, że „Pyodide jest zbudowany przez zestaw narzędzi Emscripten i można go uruchomić tylko w przeglądarce lub Node.js”.
Któregoś dnia zdecydowałem się przyjrzeć MicroPythonowi jako opcji do tego. Witryna MicroPython mówi:
MicroPython to uproszczona i wydajna implementacja języka programowania Python 3, która zawiera niewielki podzbiór standardowej biblioteki Pythona i jest zoptymalizowana do działania na mikrokontrolerach oraz w ograniczonych środowiskach.
WebAssembly z pewnością wydaje mi się ograniczonym środowiskiem!
Budowanie pierwszej wersji
Poprosiłem GPT-5.5 Pro o wykonanie pewnych badań, co spowodowało, że pojawił się ten PR przeciwko MicroPython autorstwa Yamamoto Takahashi, zatytułowany „Eksperymentalna obsługa WASI dla portów/unix”.
Następnie wygenerował ten dokument research.md, więc pozwoliłem na swobodne korzystanie z Codex Desktop i GPT-5.5, aby zobaczyć, co się stanie:
read the research.md document and build this. You will probably need to write a script that compiles a custom WASM version of MicroPython as part of this project - fetch the MicroPython code to a /tmp directory for this as part of that script.
To zadziałało. Miałem teraz prototypową bibliotekę Pythona, która mogła wykonywać kod Pythona w piaskownicy WebAssembly!
Najtrudniejszym problemem do rozwiązania był trwały stan interpretera. Kompilacja WASM, której tutaj używamy, udostępnia pojedynczy punkt wejścia, który uruchamia interpreter, uruchamia kod, a następnie zatrzymuje interpreter na końcu.
Działa to dobrze w przypadku jednorazowych skryptów, ale w przypadku agenta Datasette chcę, aby zmienne i funkcje pozostawały rezydentne w pamięci, aby móc ich ponownie używać w wielu wywołaniach wykonawczych kodu.
Zaletą pracy z agentami zajmującymi się kodowaniem jest to, że można szybko przejść od pomysłu do sprawdzenia koncepcji. podpowiedziałem:
For keeping variables resident: what if we ran code inside micropython itself which called a host function get_next_python_code() and then passed that to eval() - and that host function blocked until new code was available, maybe by running in a thread with a queue? Could that or a similar idea help here?
Po kilku iteracjach dotarliśmy do wersji, która działa! W kodzie Pythona możesz teraz to zrobić:
from micropython_wasm import MicroPythonSession with MicroPythonSession() as session: print(session.run("x = 10\nprint(x)").stdout) print(session.run("x += 5\nprint(x)").stdout) print(session.run("print(x * 2)").stdout)
W skrócie uruchamia to wątek, konfiguruje kolejkę żądań, a następnie wysyła wiadomości do tej kolejki w celu session.run() polecenie, za każdym razem oczekując w kolejce odpowiedzi na wynik tego wykonania. Wewnątrz WASM interpreter MicroPython blokuje oczekiwanie na plik __session_next__() host, aby zwrócić następny wiersz kodu, który uruchamia eval() włączone przed zadzwonieniem __session_result__({"id": request_id, "ok": True}) gdy każdy blok został pomyślnie wykonany.
Innym elementem złożoności była obsługa funkcji hosta, więc moja biblioteka Pythona mogła selektywnie udostępniać funkcje, które można następnie wywołać za pomocą kodu uruchomionego w MicroPythonie.
Codex rozwiązał ten problem za pomocą 78 linii C, które ostatecznie zostały skompilowane w blob WebAssembly o wielkości 362 KB, który dystrybuuję wraz z pakietem.
W żadnym wypadku nie jestem programistą C, ale czytałem C i miałem do czynienia z dwoma różnymi modelami, które mi to wyjaśniły (tutaj jest wyjaśnienie Claude’a), po czym poddałem go masie testów.
Wspaniałą rzeczą w pracy z WebAssembly jest to, że jeśli C okaże się fatalnie wadliwy, najgorsze, co może się zdarzyć, to wykonanie WebAssembly zakończy się niepowodzeniem, z wyjątkiem wyjątku. Mogę żyć z tym ryzykiem.
Limity pamięci są bezpośrednio obsługiwane przez wasmtime. Limity procesora są nieco większe: wasmtime oferuje koncepcję „paliwa”, która ogranicza liczbę operacji, które może wykonać wywołanie WebAssembly, i to jest właściwe rozwiązanie tego problemu, ale trudno określić jednostki. Eksperymentuję teraz z domyślnym ustawieniem „paliwa” na poziomie 20 milionów, ale nie jestem pewien, czy jest to najodpowiedniejsza wartość.
Spróbuj sam
The micropython-wasm alpha jest teraz dostępna na PyPI.
Możesz wypróbować to z własnego kodu Pythona, jak opisano w pliku README. Dodałem także prosty tryb CLI w wersji 0.1a2, co oznacza, że możesz spróbować go użyć uvx bez uprzedniej instalacji w ten sposób:
uvx micropython-wasm -c 'print("Hello world")'
# To see it run out of fuel:
uvx micropython-wasm -c 's = ""; while True: s += "longer"'
# Outputs: micropython-wasm: guest exited with code 1
Możesz także wypróbować to w agencie Datasette w następujący sposób:
uvx llm keys set openai
# Paste in an OpenAI key, then:
uvx --with datasette-agent \
--with datasette-agent-micropython \
--prerelease allow \
datasette --internal internal.db \
-s plugins.datasette-llm.default_model gpt-5.5 \
--root -o
Następnie przejdź do http://127.0.0.1:8001/-/agent i uruchom monit:
show me some micropython

Czy powinieneś zaufać mojej piaskownicy z kodem wibracyjnym?
Po narzekaniu na niedojrzałe, luźno utrzymywane biblioteki piaskownicy, zakrawa na głęboką ironię losu, że teraz zbudowałem własne!
Celowo umieściłem na nim wersję alfa i nie jestem gotowy polecać jej nikomu, kto nie chce podjąć znaczącego ryzyka.
Poddałem go wystarczającej liczbie testów, więc mogę go używać samodzielnie. Wysłałem moją pierwszą wtyczkę, która z niej korzysta, datasette-agent-micropython. Zablokowałem także GPT-5.5 xhigh we wtyczce Datasette Agent i rzuciłem mu wyzwanie, aby wyrwał się z piaskownicy, ale jak dotąd nie udało się.
Mam nadzieję, że ta implementacja przekona niektóre firmy z profesjonalnymi zespołami ds. bezpieczeństwa i poważnymi problemami do zaangażowania się w używanie Pythona w WebAssembly jako podejścia typu sandbox i open source do własnych rozwiązań.
