import sys
import json
import importlib
import traceback
from typing import Any, Dict, Optional
from pathlib import Path
_object_refs: Dict[str, Any] = {}
_ref_counter = 0
def _generate_ref_id() -> str:
global _ref_counter
_ref_counter += 1
return f"ref:{_ref_counter:06d}"
def _is_json_serializable(obj: Any) -> bool:
try:
json.dumps(obj)
return True
except (TypeError, ValueError, OverflowError):
return False
def _serialize_result(obj: Any) -> tuple[str, Any, Optional[str]]:
if obj is None:
return ("none", None, None)
if _is_json_serializable(obj):
return ("value", obj, None)
ref_id = _generate_ref_id()
_object_refs[ref_id] = obj
type_name = type(obj).__name__
module = type(obj).__module__
full_type = f"{module}.{type_name}" if module != "builtins" else type_name
return ("ref", {"__type__": full_type, "__repr__": repr(obj)[:200]}, ref_id)
def _resolve_target(target: str) -> Any:
if target.startswith("ref:"):
if target not in _object_refs:
raise ValueError(f"Unknown object reference: {target}")
return _object_refs[target]
if target.startswith("./") or target.startswith("../") or target.endswith(".py"):
path = Path(target)
if not path.exists():
raise ImportError(f"Python file not found: {target}")
spec = importlib.util.spec_from_file_location(path.stem, path)
module = importlib.util.module_from_spec(spec)
sys.modules[path.stem] = module
spec.loader.exec_module(module)
return module
return importlib.import_module(target)
def handle_call(request: Dict[str, Any]) -> Dict[str, Any]:
req_id = request.get("id", "unknown")
target = request.get("target", "")
method = request.get("method", "")
args = request.get("args", [])
kwargs = request.get("kwargs", {})
try:
obj = _resolve_target(target)
if method:
if not hasattr(obj, method):
raise AttributeError(f"'{target}' has no attribute '{method}'")
func = getattr(obj, method)
else:
func = obj
if callable(func):
result = func(*args, **kwargs)
else:
result = func
result_type, value, ref = _serialize_result(result)
return {
"id": req_id,
"type": result_type,
"value": value,
"ref": ref,
"error": None,
}
except Exception as e:
return {
"id": req_id,
"type": "error",
"value": None,
"ref": None,
"error": {
"type": type(e).__name__,
"message": str(e),
"traceback": traceback.format_exc(),
},
}
def handle_getattr(request: Dict[str, Any]) -> Dict[str, Any]:
req_id = request.get("id", "unknown")
target = request.get("target", "")
attr = request.get("attr", "")
try:
obj = _resolve_target(target)
result = getattr(obj, attr)
result_type, value, ref = _serialize_result(result)
return {
"id": req_id,
"type": result_type,
"value": value,
"ref": ref,
"error": None,
}
except Exception as e:
return {
"id": req_id,
"type": "error",
"value": None,
"ref": None,
"error": {
"type": type(e).__name__,
"message": str(e),
"traceback": traceback.format_exc(),
},
}
def handle_del(request: Dict[str, Any]) -> Dict[str, Any]:
req_id = request.get("id", "unknown")
refs = request.get("refs", [])
deleted = []
for ref in refs:
if ref in _object_refs:
del _object_refs[ref]
deleted.append(ref)
return {
"id": req_id,
"type": "value",
"value": {"deleted": deleted, "remaining": len(_object_refs)},
"ref": None,
"error": None,
}
def handle_ping(request: Dict[str, Any]) -> Dict[str, Any]:
return {
"id": request.get("id", "unknown"),
"type": "value",
"value": {"status": "ok", "refs_count": len(_object_refs)},
"ref": None,
"error": None,
}
def main():
sys.stdout = open(sys.stdout.fileno(), mode='w', buffering=1)
handlers = {
"call": handle_call,
"getattr": handle_getattr,
"del": handle_del,
"ping": handle_ping,
}
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
request = json.loads(line)
except json.JSONDecodeError as e:
response = {
"id": "unknown",
"type": "error",
"value": None,
"ref": None,
"error": {
"type": "JSONDecodeError",
"message": f"Invalid JSON: {e}",
"traceback": None,
},
}
print(json.dumps(response), flush=True)
continue
req_type = request.get("type", "call")
handler = handlers.get(req_type, handle_call)
response = handler(request)
print(json.dumps(response), flush=True)
if __name__ == "__main__":
main()