sunnypilot v2026.002.000
version: sunnypilot v2026.002.000 (staging) date: 2026-07-21T07:44:02 master commit: 2c334ede443d7391d27575af8c854a095ba702a8
This commit is contained in:
commit
de77c89cf6
3995 files changed
+1260688
No files matched your search
@@ -0,0 +1,116 @@
|
||||
import json
|
||||
from collections.abc import Callable, Mapping
|
||||
from typing import Any
|
||||
|
||||
# a minimal implementation of json-rpc 2.0 https://www.jsonrpc.org/specification
|
||||
|
||||
JSONRPC_VERSION = "2.0"
|
||||
|
||||
# JSON-RPC 2.0 reserved / application error codes
|
||||
PARSE_ERROR = -32700
|
||||
INVALID_REQUEST = -32600
|
||||
METHOD_NOT_FOUND = -32601
|
||||
INVALID_PARAMS = -32602
|
||||
SERVER_ERROR = -32000
|
||||
|
||||
JsonDict = dict[str, Any]
|
||||
MethodMap = Mapping[str, Callable[..., Any]]
|
||||
|
||||
|
||||
class Dispatcher(dict[str, Callable[..., Any]]):
|
||||
def add_method(self, f: Callable[..., Any] | None = None, *, name: str | None = None):
|
||||
def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
|
||||
self[name or fn.__name__] = fn
|
||||
return fn
|
||||
return decorator(f) if f is not None else decorator
|
||||
|
||||
|
||||
dispatcher = Dispatcher()
|
||||
|
||||
|
||||
def dumps_call(method: str, params: Any = None, request_id: Any = None) -> str:
|
||||
msg: JsonDict = {"jsonrpc": JSONRPC_VERSION, "method": method, "id": request_id}
|
||||
if params is not None:
|
||||
msg["params"] = params
|
||||
return json.dumps(msg)
|
||||
|
||||
|
||||
def dumps_result(request_id: Any, result: Any) -> str:
|
||||
return json.dumps({"jsonrpc": JSONRPC_VERSION, "id": request_id, "result": result})
|
||||
|
||||
|
||||
def dumps_error(request_id: Any, message: str, code: int = SERVER_ERROR) -> str:
|
||||
return json.dumps({
|
||||
"jsonrpc": JSONRPC_VERSION,
|
||||
"id": request_id,
|
||||
"error": {"code": code, "message": message},
|
||||
})
|
||||
|
||||
|
||||
def loads(raw: str | bytes) -> JsonDict:
|
||||
if isinstance(raw, bytes):
|
||||
raw = raw.decode()
|
||||
data = json.loads(raw)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError("message must be a JSON object")
|
||||
return data
|
||||
|
||||
|
||||
def is_call(msg: JsonDict) -> bool:
|
||||
return "method" in msg
|
||||
|
||||
|
||||
def is_response(msg: JsonDict) -> bool:
|
||||
return "id" in msg and ("result" in msg or "error" in msg)
|
||||
|
||||
|
||||
def error_message(err: Any) -> str:
|
||||
"""Normalize JSON-RPC object errors and plain-string errors."""
|
||||
if isinstance(err, str):
|
||||
return err
|
||||
if isinstance(err, dict):
|
||||
data = err.get("data")
|
||||
if isinstance(data, dict) and data.get("message"):
|
||||
return str(data["message"])
|
||||
if err.get("message") is not None:
|
||||
return str(err["message"])
|
||||
return str(err)
|
||||
|
||||
|
||||
def _invoke(fn: Callable[..., Any], params: Any) -> Any:
|
||||
if params is None:
|
||||
return fn()
|
||||
if isinstance(params, dict):
|
||||
return fn(**params)
|
||||
if isinstance(params, (list, tuple)):
|
||||
return fn(*params)
|
||||
raise TypeError("params must be a list, object, or omitted")
|
||||
|
||||
|
||||
def handle(raw: str | bytes | JsonDict, methods: MethodMap | None = None) -> str:
|
||||
methods = dispatcher if methods is None else methods
|
||||
|
||||
try:
|
||||
msg = raw if isinstance(raw, dict) else loads(raw)
|
||||
except (TypeError, ValueError, UnicodeDecodeError):
|
||||
return dumps_error(None, "parse error", PARSE_ERROR)
|
||||
|
||||
if not is_call(msg):
|
||||
raise ValueError("not a call")
|
||||
|
||||
req_id = msg.get("id")
|
||||
name = msg.get("method")
|
||||
if not isinstance(name, str):
|
||||
return dumps_error(req_id, "invalid request", INVALID_REQUEST)
|
||||
|
||||
try:
|
||||
fn = methods[name]
|
||||
except KeyError:
|
||||
return dumps_error(req_id, f"method not found: {name}", METHOD_NOT_FOUND)
|
||||
|
||||
try:
|
||||
return dumps_result(req_id, _invoke(fn, msg.get("params")))
|
||||
except TypeError as e:
|
||||
return dumps_error(req_id, str(e), INVALID_PARAMS)
|
||||
except Exception as e:
|
||||
return dumps_error(req_id, str(e), SERVER_ERROR)
|
||||
Reference in new issue
Block a user