diff --git a/CHANGELOG.md b/CHANGELOG.md index 05b22a9f..d1dd2f71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.9.1] 2024-05-14 + +### Fix + +- `importlib.resources` does not exists on Python 3.8 + ## [0.9.0] - 2024-05-06 ### Added diff --git a/speculos/api/apdu.py b/speculos/api/apdu.py index 0424cd5d..7dafbf29 100644 --- a/speculos/api/apdu.py +++ b/speculos/api/apdu.py @@ -4,8 +4,8 @@ from flask import stream_with_context, Response, request from typing import Generator, Optional +from speculos.resources_importer import get_resource_schema_as_json from ..mcu.seproxyhal import SeProxyHal -from .helpers import load_json_schema from .restful import SephResource @@ -48,7 +48,7 @@ def seph_apdu_callback(self, data: bytes) -> None: class APDU(SephResource): - schema = load_json_schema("apdu.schema") + schema = get_resource_schema_as_json("api", "apdu.schema") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/speculos/api/api.py b/speculos/api/api.py index ff4c441d..1ff5833c 100644 --- a/speculos/api/api.py +++ b/speculos/api/api.py @@ -1,6 +1,5 @@ import socket import threading -import importlib.resources from typing import Any, Dict from flask import Flask from flask_restful import Api @@ -9,6 +8,7 @@ from speculos.mcu.readerror import ReadError from speculos.mcu.seproxyhal import SeProxyHal from speculos.observer import BroadcastInterface +from speculos.resources_importer import resources from .apdu import APDU from .automation import Automation from .button import Button @@ -59,7 +59,7 @@ def __init__(self, seph: SeProxyHal, automation_server: BroadcastInterface): self._port = api_port - static_folder = str(importlib.resources.files(__package__) / "static") + static_folder = str(resources.files(__package__) / "static") self._app = Flask(__name__, static_url_path="", static_folder=static_folder) self._app.env = "development" diff --git a/speculos/api/button.py b/speculos/api/button.py index a1828da7..04511f69 100644 --- a/speculos/api/button.py +++ b/speculos/api/button.py @@ -1,12 +1,12 @@ import jsonschema from flask import request -from .helpers import load_json_schema +from speculos.resources_importer import get_resource_schema_as_json from .restful import SephResource class Button(SephResource): - schema = load_json_schema("button.schema") + schema = get_resource_schema_as_json("api", "button.schema") def post(self): args = request.get_json(force=True) diff --git a/speculos/api/finger.py b/speculos/api/finger.py index 881adbc2..2746ea0e 100644 --- a/speculos/api/finger.py +++ b/speculos/api/finger.py @@ -1,12 +1,12 @@ import jsonschema from flask import request -from .helpers import load_json_schema +from speculos.resources_importer import get_resource_schema_as_json from .restful import SephResource class Finger(SephResource): - schema = load_json_schema("finger.schema") + schema = get_resource_schema_as_json("api", "finger.schema") def post(self): args = request.get_json(force=True) diff --git a/speculos/api/helpers.py b/speculos/api/helpers.py deleted file mode 100644 index 0d6bb33f..00000000 --- a/speculos/api/helpers.py +++ /dev/null @@ -1,9 +0,0 @@ -import importlib.resources -import json - - -def load_json_schema(filename): - path = importlib.resources.files(__package__) / "resources" / filename - with path.open("rb") as fp: - schema = json.load(fp) - return schema diff --git a/speculos/api/ticker.py b/speculos/api/ticker.py index 2f227b2a..afe9a34d 100644 --- a/speculos/api/ticker.py +++ b/speculos/api/ticker.py @@ -1,12 +1,12 @@ import jsonschema from flask import request -from .helpers import load_json_schema +from speculos.resources_importer import get_resource_schema_as_json from .restful import SephResource class Ticker(SephResource): - schema = load_json_schema("ticker.schema") + schema = get_resource_schema_as_json("api", "ticker.schema") def post(self): args = request.get_json(force=True) diff --git a/speculos/main.py b/speculos/main.py index 4fd735f9..f7ba54db 100644 --- a/speculos/main.py +++ b/speculos/main.py @@ -7,7 +7,6 @@ import argparse import binascii import ctypes -import importlib.resources import logging import os import re @@ -31,6 +30,7 @@ from .mcu.struct import DisplayArgs, ServerArgs from .mcu.vnc import VNC from .observer import BroadcastInterface +from .resources_importer import resources DEFAULT_SEED = ('glory promote mansion idle axis finger extra february uncover one trip resource lawn turtle enact ' @@ -38,7 +38,7 @@ logger = logging.getLogger("speculos") -launcher_path = str(importlib.resources.files(__package__) / "resources" / "launcher") +launcher_path = str(resources.files(__package__) / "resources" / "launcher") def set_pdeath(sig): @@ -142,7 +142,7 @@ def run_qemu(s1: socket.socket, s2: socket.socket, args: argparse.Namespace, use cxlib_filepath = f"cxlib/{args.model}-api-level-cx-{args.apiLevel}.elf" else: cxlib_filepath = f"cxlib/{args.model}-cx-{args.sdk}.elf" - cxlib = str(importlib.resources.files(__package__) / cxlib_filepath) + cxlib = str(resources.files(__package__) / cxlib_filepath) if os.path.exists(cxlib): sh_offset, sh_size, sh_load, cx_ram_size, cx_ram_load = get_cx_infos(cxlib) cxlib_args = f'{cxlib}:{sh_offset:#x}:{sh_size:#x}:{sh_load:#x}:{cx_ram_size:#x}:{cx_ram_load:#x}' @@ -153,7 +153,7 @@ def run_qemu(s1: socket.socket, s2: socket.socket, args: argparse.Namespace, use # for NBGL apps, fonts binary file is mandatory if not use_bagl: fonts_filepath = f"fonts/{args.model}-fonts-{args.apiLevel}.bin" - fonts = str(importlib.resources.files(__package__) / fonts_filepath) + fonts = str(resources.files(__package__) / fonts_filepath) if os.path.exists(fonts): argv += ['-f', fonts] else: diff --git a/speculos/mcu/automation.py b/speculos/mcu/automation.py index a3065a8a..b79cb1d1 100644 --- a/speculos/mcu/automation.py +++ b/speculos/mcu/automation.py @@ -1,9 +1,10 @@ -import importlib.resources import json import jsonschema import logging import re +from speculos.resources_importer import get_resources_path + class Automation: def __init__(self, document): @@ -19,7 +20,7 @@ def __init__(self, document): self.validate() def validate(self): - path = importlib.resources.files(__package__) / "resources" / "automation.schema" + path = get_resources_path("mcu", "automation.schema") with path.open("rb") as fp: schema = json.load(fp) jsonschema.validate(instance=self.json, schema=schema) diff --git a/speculos/resources_importer.py b/speculos/resources_importer.py new file mode 100644 index 00000000..d599db87 --- /dev/null +++ b/speculos/resources_importer.py @@ -0,0 +1,22 @@ +import json +from pathlib import Path +from platform import python_version_tuple +from typing import Dict + +major, minor, _ = python_version_tuple() +assert major == "3" + +if int(minor) <= 8: + import importlib_resources as resources +else: + import importlib.resources as resources + + +def get_resources_path(module: str, filename: str) -> Path: + return resources.files(__package__) / module / "resources" / filename + + +def get_resource_schema_as_json(module: str, filename: str) -> Dict: + with get_resources_path(module, filename).open("rb") as fp: + schema = json.load(fp) + return schema diff --git a/tests/python/test_resources_importer.py b/tests/python/test_resources_importer.py new file mode 100644 index 00000000..52f6139c --- /dev/null +++ b/tests/python/test_resources_importer.py @@ -0,0 +1,14 @@ +import speculos.resources_importer as r + + +def test_api_resource_exists(): + assert r.get_resources_path("api", "").exists() + + +def test_mcu_resource_exists(): + assert r.get_resources_path("mcu", "").exists() + + +def test_load_json(): + schema = r.get_resource_schema_as_json("api", "finger.schema") + assert isinstance(schema, dict)