Spaces:
Sleeping
Sleeping
File size: 2,303 Bytes
639a496 ea655e4 639a496 ea655e4 639a496 ea655e4 639a496 ea655e4 639a496 ea655e4 639a496 9ae008a ea655e4 639a496 ea655e4 639a496 ea655e4 639a496 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | import sys
from typing import Any, Callable, Dict, Tuple
SAFE_BUILTINS = {
"abs": abs,
"bool": bool,
"dict": dict,
"enumerate": enumerate,
"float": float,
"int": int,
"list": list,
"len": len,
"max": max,
"min": min,
"print": print,
"range": range,
"str": str,
"sum": sum,
"tuple": tuple,
}
MAX_SOURCE_CHARS = 20_000
DEFAULT_MAX_EXECUTION_LINES = 4_000
class ExecutionLimitExceeded(RuntimeError):
pass
def _run_with_line_limit(fn: Callable[[], Any], max_executed_lines: int) -> Any:
counter = {"lines": 0}
def tracer(frame, event, arg): # noqa: ANN001
if event == "line":
counter["lines"] += 1
if counter["lines"] > max_executed_lines:
raise ExecutionLimitExceeded(
f"Execution limit exceeded ({max_executed_lines} lines)."
)
return tracer
previous_tracer = sys.gettrace()
sys.settrace(tracer)
try:
return fn()
finally:
sys.settrace(previous_tracer)
def compile_code(code: str) -> Tuple[bool, str]:
if len(code) > MAX_SOURCE_CHARS:
return False, f"Code exceeds max length of {MAX_SOURCE_CHARS} characters."
try:
compile(code, "<candidate>", "exec")
return True, ""
except SyntaxError as exc:
return False, f"SyntaxError: {exc.msg} (line {exc.lineno})"
def execute_code(code: str, max_executed_lines: int = DEFAULT_MAX_EXECUTION_LINES) -> Tuple[bool, Dict[str, Any], str]:
syntax_ok, syntax_error = compile_code(code)
if not syntax_ok:
return False, {}, syntax_error
namespace: Dict[str, Any] = {"__builtins__": SAFE_BUILTINS}
try:
_run_with_line_limit(
lambda: exec(code, namespace, namespace),
max_executed_lines=max_executed_lines,
)
except ExecutionLimitExceeded as exc:
return False, {}, str(exc)
except Exception as exc:
return False, {}, f"{exc.__class__.__name__}: {exc}"
return True, namespace, ""
def invoke_callable(
fn: Callable[..., Any],
*args: Any,
max_executed_lines: int = DEFAULT_MAX_EXECUTION_LINES,
) -> Any:
return _run_with_line_limit(
lambda: fn(*args),
max_executed_lines=max_executed_lines,
) |