Coverage for aipyapp/exec/prun.py: 51%

45 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-11 12:02 +0200

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3 

4""" subprocess-based bash/powershell code execution """ 

5 

6import traceback 

7import subprocess 

8from typing import Any, Dict, Optional 

9 

10from loguru import logger 

11 

12class SubprocessExecutor: 

13 """使用 subprocess 执行代码块""" 

14 name = None 

15 command = None 

16 timeout = 30 # 默认超时时间为10秒 

17 

18 def __init__(self, runtime=None): 

19 self.runtime = runtime 

20 self.log = logger.bind(src=f'{self.name}_executor') 

21 

22 def get_cmd(self, block) -> Optional[str]: 

23 """获取执行命令""" 

24 path = block.abs_path 

25 if path: 

26 cmd = self.command.copy() 

27 cmd.append(str(path)) 

28 else: 

29 cmd = None 

30 return cmd 

31 

32 def __call__(self, block) -> Dict[str, Any]: 

33 """执行代码块""" 

34 cmd = self.get_cmd(block) 

35 if not cmd: 

36 return {'errstr': 'No file to execute'} 

37 

38 self.log.info(f"Exec: {cmd}") 

39 

40 try: 

41 cp = subprocess.run( 

42 cmd, 

43 shell=False, 

44 capture_output=True, 

45 text=True, 

46 encoding="utf-8", 

47 errors="ignore", 

48 timeout=self.timeout 

49 ) 

50 stdout = cp.stdout.strip() if cp.stdout else None 

51 stderr = cp.stderr.strip() if cp.stderr else None 

52 

53 result = { 

54 'stdout': stdout, 

55 'stderr': stderr, 

56 'returncode': cp.returncode 

57 } 

58 except subprocess.TimeoutExpired: 

59 result = {'errstr': f'Execution timed out after {self.timeout} seconds'} 

60 except Exception as e: 

61 result = {'errstr': str(e), 'traceback': str(traceback.format_exc())} 

62 

63 return result 

64 

65class BashExecutor(SubprocessExecutor): 

66 name = 'bash' 

67 command = ['bash'] 

68 

69class PowerShellExecutor(SubprocessExecutor): 

70 name = 'powershell' 

71 command = ['powershell', '-Command'] 

72 

73class AppleScriptExecutor(SubprocessExecutor): 

74 name = 'applescript' 

75 command = ['osascript'] 

76 

77class NodeExecutor(SubprocessExecutor): 

78 name = 'javascript' 

79 command = ['node']