From 1f0de9b3540053704e975f64f525ac6905b20007 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:37:48 -0500 Subject: [PATCH] fix(starlark): fix Python 3.13 importlib.reload() incompatibility (#258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(starlark): fix Python 3.13 importlib.reload() incompatibility In Python 3.13, importlib.reload() raises ModuleNotFoundError for modules loaded via spec_from_file_location when they aren't on sys.path, because _bootstrap._find_spec() can no longer resolve them by name. Replace the reload-on-cache-hit pattern in _get_tronbyte_repository_class() and _get_pixlet_renderer_class() with a simple return of the cached class — the reload was only useful for dev-time iteration and is unnecessary in production (the service restarts clean on each deploy). Also broaden the exception catch in upload_starlark_app() from (ValueError, OSError, IOError) to Exception so that any unexpected error (ImportError, ModuleNotFoundError, etc.) returns a proper JSON response instead of an unhandled Flask 500. Fixes: "Install failed: spec not found for the module 'tronbyte_repository'" Co-Authored-By: Claude Sonnet 4.6 * fix(starlark): use targeted exception handlers in upload_starlark_app() Replace the broad `except Exception` catch-all with specific handlers: - (OSError, IOError) for temp file creation/save failures - ImportError for module loading failures (_get_pixlet_renderer_class) - Exception as final catch-all that logs without leaking internals All handlers use `err` (not unused `e`) in both the log message and the JSON response body. Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Chuck Co-authored-by: Claude Sonnet 4.6 --- web_interface/blueprints/api_v3.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/web_interface/blueprints/api_v3.py b/web_interface/blueprints/api_v3.py index 36b10d7a..fa364146 100644 --- a/web_interface/blueprints/api_v3.py +++ b/web_interface/blueprints/api_v3.py @@ -6989,9 +6989,8 @@ def _get_tronbyte_repository_class() -> Type[Any]: if not module_path.exists(): raise ImportError(f"TronbyteRepository module not found at {module_path}") - # If already imported, reload to pick up code changes + # If already imported, return cached class if "tronbyte_repository" in sys.modules: - importlib.reload(sys.modules["tronbyte_repository"]) return sys.modules["tronbyte_repository"].TronbyteRepository spec = importlib.util.spec_from_file_location("tronbyte_repository", str(module_path)) @@ -7016,9 +7015,8 @@ def _get_pixlet_renderer_class() -> Type[Any]: if not module_path.exists(): raise ImportError(f"PixletRenderer module not found at {module_path}") - # If already imported, reload to pick up code changes + # If already imported, return cached class if "pixlet_renderer" in sys.modules: - importlib.reload(sys.modules["pixlet_renderer"]) return sys.modules["pixlet_renderer"].PixletRenderer spec = importlib.util.spec_from_file_location("pixlet_renderer", str(module_path)) @@ -7442,8 +7440,14 @@ def upload_starlark_app(): except OSError: pass - except (ValueError, OSError, IOError) as e: - logger.exception("[Starlark] Error uploading starlark app") + except (OSError, IOError) as err: + logger.exception("[Starlark] File error uploading starlark app: %s", err) + return jsonify({'status': 'error', 'message': f'File error during upload: {err}'}), 500 + except ImportError as err: + logger.exception("[Starlark] Module load error uploading starlark app: %s", err) + return jsonify({'status': 'error', 'message': f'Failed to load app module: {err}'}), 500 + except Exception as err: + logger.exception("[Starlark] Unexpected error uploading starlark app: %s", err) return jsonify({'status': 'error', 'message': 'Failed to upload app'}), 500