From 46a3a24ce4642829c354c9ca5183ca4b630ab508 Mon Sep 17 00:00:00 2001 From: arcbtc Date: Wed, 11 Mar 2026 23:38:38 +0000 Subject: [PATCH] make add wasmtime bundle mypy fix mypy mypy pyright pyright pyright make lock file --- lnbits/app.py | 40 +++++++++++---- lnbits/core/helpers.py | 6 ++- lnbits/core/views/extension_api.py | 47 +++++++++++------ lnbits/static/js/pages/extensions.js | 3 +- pyproject.toml | 7 +-- uv.lock | 76 +++++++++++++++++++++++++++- 6 files changed, 146 insertions(+), 33 deletions(-) diff --git a/lnbits/app.py b/lnbits/app.py index 15b23644a..4561a9bd5 100644 --- a/lnbits/app.py +++ b/lnbits/app.py @@ -1,6 +1,7 @@ import asyncio import glob import importlib +import json import os import shutil import sys @@ -53,10 +54,6 @@ from lnbits.utils.logger import ( log_server_info, ) from lnbits.wallets import get_funding_source, set_funding_source -try: - from lnbits.extensions.wasm.wasm_host.extension_host import register_wasm_ext_routes -except Exception: # pragma: no cover - optional parent extension - register_wasm_ext_routes = None from .commands import migrate_databases from .core import init_core_routers @@ -442,12 +439,23 @@ def register_ext_routes(app: FastAPI, ext: Extension) -> None: ext.extension_type = _load_extension_type(ext.code) or ext.extension_type if ext.extension_type == "wasm": settings.activate_extension_paths(ext.code, ext.upgrade_hash, []) - if register_wasm_ext_routes is None: + try: + module = importlib.import_module( + "lnbits.extensions.wasm.wasm_host.extension_host" + ) + register_wasm_ext_routes = getattr(module, "register_wasm_ext_routes", None) + except Exception: # pragma: no cover - optional parent extension logger.error( "WASM host extension not installed; cannot register wasm extension " f"{ext.code}." ) return + if register_wasm_ext_routes is None: + logger.error( + "WASM host extension missing register_wasm_ext_routes; cannot " + f"register wasm extension {ext.code}." + ) + return register_wasm_ext_routes(app, ext) return ext_module = importlib.import_module(ext.module_name) @@ -488,20 +496,34 @@ def _load_extension_type(ext_id: str) -> str | None: try: conf_path = base / "config.json" if conf_path.is_file(): - with open(conf_path, "r") as json_file: + with open(conf_path) as json_file: config_json = json.load(json_file) ext_type = config_json.get("extension_type") if ext_type: return ext_type - except Exception: + except Exception as exc: + logger.debug( + "Failed to read extension config.json for '{}' in '{}': {}", + ext_id, + base, + exc, + ) continue for base in base_dirs: try: wasm_dir = base / "wasm" - if (wasm_dir / "module.wasm").is_file() or (wasm_dir / "module.wat").is_file(): + if (wasm_dir / "module.wasm").is_file() or ( + wasm_dir / "module.wat" + ).is_file(): return "wasm" - except Exception: + except Exception as exc: + logger.debug( + "Failed to probe wasm files for '{}' in '{}': {}", + ext_id, + base, + exc, + ) continue return None diff --git a/lnbits/core/helpers.py b/lnbits/core/helpers.py index 1c878219f..7e4ce402b 100644 --- a/lnbits/core/helpers.py +++ b/lnbits/core/helpers.py @@ -57,13 +57,15 @@ def _is_wasm_extension(ext: InstallableExtension) -> bool: conf_path = Path(base, "config.json") if not conf_path.is_file(): continue - with open(conf_path, "r") as json_file: + with open(conf_path) as json_file: config_json = json.load(json_file) if config_json.get("extension_type") == "wasm": return True for base in candidate_dirs: wasm_dir = Path(base, "wasm") - if (wasm_dir / "module.wasm").is_file() or (wasm_dir / "module.wat").is_file(): + if (wasm_dir / "module.wasm").is_file() or ( + wasm_dir / "module.wat" + ).is_file(): return True except Exception as exc: logger.debug(f"Failed to load extension config for '{ext.id}': {exc!s}") diff --git a/lnbits/core/views/extension_api.py b/lnbits/core/views/extension_api.py index c140931b4..d35a05f79 100644 --- a/lnbits/core/views/extension_api.py +++ b/lnbits/core/views/extension_api.py @@ -50,6 +50,19 @@ from lnbits.decorators import ( ) from lnbits.settings import settings +from ..crud import ( + create_user_extension, + delete_dbversion, + drop_extension_db, + get_db_version, + get_db_versions, + get_installed_extension, + get_installed_extensions, + get_user_extension, + update_installed_extension, + update_user_extension, +) + def _load_extension_type(ext_id: str) -> str: base_dirs = [ @@ -65,35 +78,37 @@ def _load_extension_type(ext_id: str) -> str: conf_path = base / "config.json" if not conf_path.is_file(): continue - with open(conf_path, "r") as json_file: + with open(conf_path) as json_file: config_json = json.load(json_file) ext_type = config_json.get("extension_type") if ext_type: return ext_type - except Exception: + except Exception as exc: + logger.debug( + "Failed to read extension config.json for '{}' in '{}': {}", + ext_id, + base, + exc, + ) continue for base in base_dirs: try: wasm_dir = base / "wasm" - if (wasm_dir / "module.wasm").is_file() or (wasm_dir / "module.wat").is_file(): + if (wasm_dir / "module.wasm").is_file() or ( + wasm_dir / "module.wat" + ).is_file(): return "wasm" - except Exception: + except Exception as exc: + logger.debug( + "Failed to probe wasm files for '{}' in '{}': {}", + ext_id, + base, + exc, + ) continue return "python" -from ..crud import ( - create_user_extension, - delete_dbversion, - drop_extension_db, - get_db_version, - get_db_versions, - get_installed_extension, - get_installed_extensions, - get_user_extension, - update_installed_extension, - update_user_extension, -) extension_router = APIRouter( tags=["Extension Managment"], diff --git a/lnbits/static/js/pages/extensions.js b/lnbits/static/js/pages/extensions.js index 1b976575a..8763b9a11 100644 --- a/lnbits/static/js/pages/extensions.js +++ b/lnbits/static/js/pages/extensions.js @@ -268,7 +268,8 @@ window.PageExtensions = { if (!wasmHost || !wasmHost.isInstalled || !wasmHost.isActive) { Quasar.Notify.create({ type: 'warning', - message: 'Enable the WASM! host extension before using this extension.' + message: + 'Enable the WASM! host extension before using this extension.' }) return } diff --git a/pyproject.toml b/pyproject.toml index ce2c1ba8a..5102b020c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,9 +60,10 @@ lnbits = "lnbits.server:main" lnbits-cli = "lnbits.commands:main" [project.optional-dependencies] -breez = ["breez-sdk~=0.8.0", "breez-sdk-liquid~=0.11.11"] -liquid = ["wallycore~=1.5.1"] -migration = ["psycopg2-binary~=2.9.11"] +breez = ["breez-sdk==0.8.0", "breez-sdk-liquid==0.11.11"] +liquid = ["wallycore==1.5.1"] +migration = ["psycopg2-binary==2.9.11"] +wasm = ["wasmtime>=18.0.0"] [dependency-groups] dev = [ diff --git a/uv.lock b/uv.lock index b7f666a66..a5f8b1ecd 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.10, <3.13" [options] @@ -1335,6 +1335,9 @@ liquid = [ migration = [ { name = "psycopg2-binary" }, ] +wasm = [ + { name = "wasmtime" }, +] [package.dev-dependencies] dev = [ @@ -1361,6 +1364,7 @@ dev = [ [package.metadata] requires-dist = [ +<<<<<<< HEAD { name = "aiosqlite", specifier = "~=0.22.1" }, { name = "asyncpg", specifier = "~=0.31.0" }, { name = "bcrypt", specifier = "~=5.0.0" }, @@ -1409,8 +1413,57 @@ requires-dist = [ { name = "wallycore", marker = "extra == 'liquid'", specifier = "~=1.5.1" }, { name = "websocket-client", specifier = "~=1.9.0" }, { name = "websockets", specifier = "~=15.0.1" }, +======= + { name = "aiosqlite", specifier = "==0.22.1" }, + { name = "asyncpg", specifier = "==0.31.0" }, + { name = "bcrypt", specifier = "==5.0.0" }, + { name = "bech32", specifier = "==1.2.0" }, + { name = "bolt11", specifier = "==2.1.1" }, + { name = "breez-sdk", marker = "extra == 'breez'", specifier = "==0.8.0" }, + { name = "breez-sdk-liquid", marker = "extra == 'breez'", specifier = "==0.11.11" }, + { name = "click", specifier = "==8.3.1" }, + { name = "embit", specifier = "==0.8.0" }, + { name = "fastapi", specifier = "==0.116.1" }, + { name = "fastapi-sso", specifier = "==0.19.0" }, + { name = "filetype", specifier = "==1.2.0" }, + { name = "greenlet", specifier = ">=3.3.0,<4.0.0" }, + { name = "grpcio", specifier = "==1.76.0" }, + { name = "httpx", specifier = "==0.27.2" }, + { name = "itsdangerous", specifier = "==2.2.0" }, + { name = "jinja2", specifier = "==3.1.6" }, + { name = "jsonpath-ng", specifier = "==1.7.0" }, + { name = "lnurl", specifier = "==0.8.3" }, + { name = "loguru", specifier = "==0.7.3" }, + { name = "nostr-sdk", specifier = "==0.44.0" }, + { name = "packaging", specifier = "==25.0" }, + { name = "pillow", specifier = ">=12.1.0" }, + { name = "protobuf", specifier = "==6.33.2" }, + { name = "psycopg2-binary", marker = "extra == 'migration'", specifier = "==2.9.11" }, + { name = "pycryptodomex", specifier = "==3.23.0" }, + { name = "pydantic", specifier = "==1.10.26" }, + { name = "pyjwt", specifier = "==2.10.1" }, + { name = "pyln-client", specifier = "==25.12" }, + { name = "pynostr", specifier = "==0.7.0" }, + { name = "pyqrcode", specifier = "==1.2.1" }, + { name = "python-crontab", specifier = "==3.3.0" }, + { name = "python-dotenv", specifier = ">=1.2.1" }, + { name = "python-multipart", specifier = "==0.0.21" }, + { name = "pywebpush", specifier = "==2.2.0" }, + { name = "shortuuid", specifier = "==1.0.13" }, + { name = "slowapi", specifier = "==0.1.9" }, + { name = "sqlalchemy", specifier = "==1.4.54" }, + { name = "sse-starlette", specifier = "==2.3.6" }, + { name = "starlette", specifier = "==0.47.1" }, + { name = "typing-extensions", specifier = "==4.15.0" }, + { name = "uvicorn", specifier = "==0.40.0" }, + { name = "uvloop", specifier = "==0.22.1" }, + { name = "wallycore", marker = "extra == 'liquid'", specifier = "==1.5.1" }, + { name = "wasmtime", marker = "extra == 'wasm'", specifier = ">=18.0.0" }, + { name = "websocket-client", specifier = "==1.9.0" }, + { name = "websockets", specifier = "==15.0.1" }, +>>>>>>> 3aa01d9f9 (make) ] -provides-extras = ["breez", "liquid", "migration"] +provides-extras = ["breez", "liquid", "migration", "wasm"] [package.metadata.requires-dev] dev = [ @@ -2843,6 +2896,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/e5/1c1d2979d074cba4f0a1516f2bf4c3ee66067b6ba3b96e5cb4460555d474/wallycore-1.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:c3343a1df11a7ef4572521e002f4550a0157aea2c749dcfd9be62fb5babe0d03", size = 1728038, upload-time = "2026-04-15T22:04:36.434Z" }, ] +[[package]] +name = "wasmtime" +version = "42.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/cd/1f110419ed006f91624010f4df4da82490220bd5527650284c97fc758a6c/wasmtime-42.0.0.tar.gz", hash = "sha256:90485655d6e541b817a7baa1b3071b4525d03f76bcb6ad04661774f06a3b02d4", size = 117133, upload-time = "2026-02-24T19:12:53.321Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/cb/f206f7a839d6843b01c041000056bf7aad23cf72fe2333a0c5dad144e0f2/wasmtime-42.0.0-py3-none-android_26_arm64_v8a.whl", hash = "sha256:214e7d294ce1b5adb94f09a870a2ab6759173dc0194bdde74ee4492b477d8392", size = 6829706, upload-time = "2026-02-24T19:12:36.637Z" }, + { url = "https://files.pythonhosted.org/packages/2d/97/d4f5f46eef74e013c3a0caa9b8625bb1c4162e2b9817258596ee6932c019/wasmtime-42.0.0-py3-none-android_26_x86_64.whl", hash = "sha256:cdd9710fad242dde7cb0eacbe48bf902bb1bac6ecbecd3e743c31af463a795c6", size = 7699640, upload-time = "2026-02-24T19:12:38.471Z" }, + { url = "https://files.pythonhosted.org/packages/c6/d2/5b2bf901b0a9b8050d966dff61e353de7cd86dd58679a79e48372ff8b3a6/wasmtime-42.0.0-py3-none-any.whl", hash = "sha256:7a166bd262608806f3295343fcd07ee3e037f931f6d3b0a24ab1cfc7ccc3e8eb", size = 6403639, upload-time = "2026-02-24T19:12:39.777Z" }, + { url = "https://files.pythonhosted.org/packages/3c/6f/a40322bdd55809441bab7e1ac707aa38ced3572904a700f1dfb4b2520dcd/wasmtime-42.0.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:21e3dafd74704de0e7ed7668ab76cc5a9df130b4306befbfcb08ddb29673c784", size = 7483525, upload-time = "2026-02-24T19:12:41.422Z" }, + { url = "https://files.pythonhosted.org/packages/47/04/ef61af9fe9e5c0a8d782c8662302535ee6e6dba1a6929191fa3ea371a491/wasmtime-42.0.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:411bf05df47c8a36c6b31b6012720ac1251b95fdd155e389b25eb6fbbd7e181c", size = 6493225, upload-time = "2026-02-24T19:12:42.9Z" }, + { url = "https://files.pythonhosted.org/packages/44/54/a774313c19c1c0ae2c1897af697c12178904d67911f42c4a9bdddba68640/wasmtime-42.0.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:ca12269ee88aac6b1f64b5f324abf3c6370ff853338d991292f10cb17b906667", size = 7740997, upload-time = "2026-02-24T19:12:44.453Z" }, + { url = "https://files.pythonhosted.org/packages/ed/5d/fae28526b1d42f0365e4fd6c2a212c7c000e47d7320632018fa45735a06e/wasmtime-42.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:78f9353b9fdc2f6e7ed13e28ce0394533f5a62710b75c00434ac82681f738924", size = 6785820, upload-time = "2026-02-24T19:12:45.777Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ae/5c5e96273a36c70753e8ba4db323dd9b1ccf6fcea4ccad99d458ad2ecf13/wasmtime-42.0.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ba317e879aab71c407e7012f4dc10b221c6daf737496c501005612e11d26e8ee", size = 6810021, upload-time = "2026-02-24T19:12:47.453Z" }, + { url = "https://files.pythonhosted.org/packages/46/68/5c129389f67219a90c3ba0dcf85555249bde9797760f2d715bec03bc198a/wasmtime-42.0.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9ef6dbd1a2cff21694ba64f27b90a7ab0af61a54d911a59682005830683dc8a", size = 7779984, upload-time = "2026-02-24T19:12:48.642Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e5/6650c9e7ad904c9a6730c4b762b1dfed4f7d7b0e981e3624a6ecd7abb7ed/wasmtime-42.0.0-py3-none-win_amd64.whl", hash = "sha256:3a360a1285457021efe24369490cd719996596f2cbe1aa62dae6ad68179cf0f9", size = 6403647, upload-time = "2026-02-24T19:12:50.373Z" }, + { url = "https://files.pythonhosted.org/packages/44/b2/e93046661deef4d8fee2f40080a28e5ff201cc98d4fb1929a46367c34778/wasmtime-42.0.0-py3-none-win_arm64.whl", hash = "sha256:8caa13a6ee264969449c008da1dcb8f9f6c954800853527714e7fcddbdda9166", size = 5397896, upload-time = "2026-02-24T19:12:51.639Z" }, +] + [[package]] name = "websocket-client" version = "1.9.0"