Skip to content
Snippets Groups Projects
run.py 4.50 KiB
#!/usr/bin/env python3

import importlib
import importlib.abc
import importlib.machinery
from importlib.machinery import PathFinder, BuiltinImporter
import importlib.util
import os
import sys
import builtins
import argparse
import traceback


projectpath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

import random
import pygame
import cmath
import gzip
import wasmer
import wasmer_compiler_cranelift

try:
    import config
except:
    pass

try:
    import requests
except ImportError:
    print("Warning: `requests` is missing so no `urequests` mock will exist")

try:
    import mad
except ImportError:
    print("Warning: `mad` is missing, MP3 support in `media` mock will be limited")

sys_path_orig = sys.path


class UnderscoreFinder(importlib.abc.MetaPathFinder):
    def __init__(self, builtin, pathfinder):
        self.builtin = builtin
        self.pathfinder = pathfinder

    def find_spec(self, fullname, path, target=None):
        if fullname == "_time":
            return self.builtin.find_spec("time", path, target)
        if fullname in ["random", "math"]:
            return self.builtin.find_spec(fullname, path, target)
        if fullname in ["json", "tarfile"]:
            sys_path_saved = sys.path
            sys.path = sys_path_orig
            res = self.pathfinder.find_spec(fullname, path, target)
            sys.path = sys_path_saved
            return res


# sys.meta_path.insert(0, Hook())

sys.path = [
    os.path.join(projectpath, "sim", "fakes"),
    os.path.join(projectpath, "python_payload"),
    os.path.join(projectpath, "components", "micropython", "frozen"),
]

builtin = BuiltinImporter()
pathfinder = PathFinder()
underscore = UnderscoreFinder(builtin, pathfinder)
sys.meta_path = [pathfinder, underscore]

# Clean up whatever might have already been imported as `time`.
import time

importlib.reload(time)

sys.path_importer_cache.clear()
importlib.invalidate_caches()

sys.modules["time"] = time

simpath = "/tmp/flow3r-sim"
print(f"Using {simpath} as /flash mount")
try:
    os.mkdir(simpath)
except:
    pass


def _path_replace(p):
    if p.startswith("/flash/sys"):
        p = p[len("/flash/sys") :]
        p = projectpath + "/python_payload" + p
        return p
    if p.startswith("/flash"):
        p = p[len("/flash") :]
        p = simpath + p
        return p

    return p


def _mkmock(fun):
    orig = fun

    def _wrap(path, *args, **kwargs):
        path = _path_replace(path)
        return orig(path, *args, **kwargs)

    return _wrap


def _mkmock2(fun):
    orig = fun

    def _wrap(path1, path2, *args, **kwargs):
        path1 = _path_replace(path1)
        path2 = _path_replace(path2)
        return orig(path1, path2, *args, **kwargs)

    return _wrap


os.listdir = _mkmock(os.listdir)
os.rename = _mkmock2(os.rename)
os.stat = _mkmock(os.stat)
os.statvfs = _mkmock(os.statvfs)
os.mkdir = _mkmock(os.mkdir)
os.rmdir = _mkmock(os.rmdir)
os.unlink = _mkmock(os.unlink)
os.remove = _mkmock(os.remove)
builtins.open = _mkmock(builtins.open)

orig_stat = os.stat


def _stat(path):
    res = orig_stat(path)
    # lmao
    return os.stat_result((res.st_mode, 0, 0, 0, 0, 0, res.st_size, 0, res.st_mtime, 0))


os.stat = _stat


sys.print_exception = lambda x: print(traceback.format_exc())


def sim_main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--screenshot",
        action="store_true",
        default=False,
        help="Generate a flow3r.png screenshot.",
    )
    parser.add_argument(
        "--full-screen",
        dest="full_screen",
        action="store_true",
        default=False,
        help="Run the simulator as full-screen OLED display.",
    )
    parser.add_argument("--oled-size", dest="oled_size", default=240)
    parser.add_argument("--oled-aspect", dest="oled_aspect", default=1)
    parser.add_argument("--oled-scale", dest="oled_scale", default=1)
    parser.add_argument(
        "override_app",
        nargs="?",
        help="Bundle to start instead of the main menu. "
        + "This is the `app.name` from flow3r.toml.",
    )
    args = parser.parse_args()

    os.environ["SIM_FULL_SCREEN"] = "1" if args.full_screen else "0"
    os.environ["SIM_OLED_SIZE"] = str(args.oled_size)
    os.environ["SIM_OLED_ASPECT"] = str(args.oled_aspect)
    os.environ["SIM_OLED_SCALE"] = str(args.oled_scale)
    import _sim

    _sim.SCREENSHOT = args.screenshot

    if args.override_app is not None:
        import st3m.run

        st3m.run.override_main_app = args.override_app

    import main


if __name__ == "__main__":
    sim_main()