Python

  • Template: cart_python - press "Use this template" and you have a working game.
  • Docker image: ghcr.io/notnullgames/null0-cart-python:latest
  • Your code: cart/main.py, interpreted by RustPython, baked into main.wasm
  • Bindings: carts/python/null0.pyi (editor definitions - the runtime provides the API itself)
  • WASI is available, so ordinary file and stdio calls work.
  • See carts made with Python
  • API docs for Python

building

You do not install RustPython, baked into main.wasm - the docker image has it, and the current null0 bindings, baked in. There is nothing in your project to keep in sync with the engine.

docker run --rm --user $(id -u):$(id -g) \
  -v ./cart:/src -v ./webroot:/out \
  ghcr.io/notnullgames/null0-cart-python:latest mygame

The template wraps that in npm start, which also serves the result and rebuilds when you save.

Python notes

  • The API is plain globals - nothing to import. Structs are dicts, and array args know their own length.
  • This is RustPython built without the stdlib: there is no typing, no os, no json.
  • Drop null0.pyi next to main.py and open your cart with TYPE_CHECKING = False / if TYPE_CHECKING: from null0 import * for completion.

a cart, in Python

This is carts/python/simple/main.py in the engine repo - the same file CI builds into the cart below, so it always compiles.

# the null0 API is available as plain globals - nothing to import at runtime.
# drop null0.pyi next to this file and open with the type-checking-only import
# below for editor completion (it never runs, and this RustPython has no
# `typing` module to import from).

TYPE_CHECKING = False

if TYPE_CHECKING:
    from null0 import *


def load():
    clear(BLUE)
    draw_circle(100, 100, 50, RED)
    draw_text(FONT_DEFAULT, 'hello from Python', 170, 92, WHITE)


# other callbacks you can define:
#
#   def update(): pass
#   def unload(): pass
#   def buttonDown(button, player): pass
carts/python/simple/main.py

callbacks

A cart implements the callbacks it cares about. In Python they look like this:

def update():

See anatomy of a cart for the full list and what each one is passed.