Execute an existing Python script
(
ctx: Context,
script_path: str,
args: Optional[List[str]] = None,
timeout: int = 30,
*,
cwd_path: Optional[str] = None, # defaults to script's parent
)
| 144 | |
| 145 | |
| 146 | async def execute_python_script( |
| 147 | ctx: Context, |
| 148 | script_path: str, |
| 149 | args: Optional[List[str]] = None, |
| 150 | timeout: int = 30, |
| 151 | *, |
| 152 | cwd_path: Optional[str] = None, # defaults to script's parent |
| 153 | ) -> dict: |
| 154 | """ |
| 155 | Execute an existing Python script |
| 156 | """ |
| 157 | sid = getattr(ctx, "session_id", None) |
| 158 | if not sid: |
| 159 | return _err("session_id_REQUIRED", message="ctx.session_id is missing") |
| 160 | |
| 161 | rt = MANAGER.get(sid) |
| 162 | env = rt.minimal_env() |
| 163 | tout = _clamp_timeout(timeout) |
| 164 | |
| 165 | try: |
| 166 | sp = (rt.root / script_path.lstrip("/")).resolve() |
| 167 | if not sp.exists(): |
| 168 | return _err( |
| 169 | "FILE_NOT_FOUND", message=f"Script does not exist: {script_path}" |
| 170 | ) |
| 171 | if sp.suffix.lower() != ".py": |
| 172 | return _err( |
| 173 | "INVALID_ARGUMENT", message=f"File must be a .py script: {sp.name}" |
| 174 | ) |
| 175 | |
| 176 | cwd = (rt.root / cwd_path.lstrip("/")).resolve() if cwd_path else sp.parent |
| 177 | |
| 178 | cmd = ["python3", str(sp)] |
| 179 | if args: |
| 180 | cmd.extend([str(a) for a in args]) |
| 181 | |
| 182 | result = { |
| 183 | "stdout": "", |
| 184 | "stderr": "", |
| 185 | "return_code": -1, |
| 186 | "execution_time": 0.0, |
| 187 | "script_path": script_path, |
| 188 | } |
| 189 | |
| 190 | start = time.time() |
| 191 | proc = await asyncio.create_subprocess_exec( |
| 192 | *cmd, |
| 193 | stdout=asyncio.subprocess.PIPE, |
| 194 | stderr=asyncio.subprocess.PIPE, |
| 195 | cwd=str(cwd), |
| 196 | env=env, |
| 197 | ) |
| 198 | |
| 199 | try: |
| 200 | stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=tout) |
| 201 | result["execution_time"] = time.time() - start |
| 202 | result["return_code"] = proc.returncode |
| 203 |
nothing calls this directly
no test coverage detected