Coverage for src / moai_adk / core / performance / parallel_processor.py: 0.00%

46 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-20 20:52 +0900

1""" 

2Parallel Processing Core 

3 

4Provides efficient parallel processing capabilities for concurrent task execution. 

5""" 

6 

7import asyncio 

8from typing import Any, Callable, Coroutine, Dict, List, Optional, Union 

9 

10 

11class ParallelProcessor: 

12 """ 

13 A parallel processor for executing tasks concurrently with configurable limits. 

14 

15 This class provides a high-level interface for running multiple async tasks 

16 in parallel with optional progress tracking and error handling. 

17 """ 

18 

19 def __init__(self, max_workers: Optional[int] = None): 

20 """ 

21 Initialize the parallel processor. 

22 

23 Args: 

24 max_workers: Maximum number of concurrent tasks. If None, defaults to CPU count. 

25 """ 

26 self.max_workers = max_workers 

27 

28 async def process_tasks( 

29 self, 

30 tasks: List[Callable], 

31 progress_callback: Optional[Callable[[int, int], None]] = None, 

32 ) -> List[Dict[str, Any]]: 

33 """ 

34 Process multiple tasks concurrently. 

35 

36 Args: 

37 tasks: List of async task functions or coroutines to execute 

38 progress_callback: Optional callback for progress updates (completed, total) 

39 

40 Returns: 

41 List of results from all completed tasks in the same order as input 

42 

43 Raises: 

44 Exception: If any task raises an exception 

45 """ 

46 if not tasks: 

47 return [] 

48 

49 results = [] 

50 completed_count = 0 

51 

52 # Validate input 

53 self._validate_tasks(tasks) 

54 

55 # Initialize progress tracking 

56 self._update_progress(progress_callback, completed_count, len(tasks)) 

57 

58 # Process tasks concurrently 

59 for task in tasks: 

60 try: 

61 # Get coroutine from task 

62 coroutine = self._get_coroutine(task) 

63 

64 # Execute the task and get result 

65 result = await coroutine 

66 results.append(result) 

67 completed_count += 1 

68 

69 # Update progress 

70 self._update_progress(progress_callback, completed_count, len(tasks)) 

71 

72 except Exception as e: 

73 # Add context to the exception 

74 raise type(e)(f"Task failed: {str(e)}") from e 

75 

76 return results 

77 

78 def _validate_tasks(self, tasks: List[Callable]) -> None: 

79 """Validate that all tasks are callable or coroutines.""" 

80 if not isinstance(tasks, list): 

81 raise TypeError("tasks must be a list") 

82 

83 for i, task in enumerate(tasks): 

84 if not callable(task) and not asyncio.iscoroutine(task): 

85 raise TypeError(f"Task at index {i} is not callable or a coroutine") 

86 

87 def _get_coroutine(self, task: Union[Callable, Coroutine]) -> Coroutine: 

88 """Extract coroutine from task function or return coroutine directly.""" 

89 if asyncio.iscoroutine(task): 

90 return task 

91 

92 if callable(task): 

93 try: 

94 result = task() 

95 if asyncio.iscoroutine(result): 

96 return result 

97 else: 

98 raise TypeError("Task function must return a coroutine") 

99 except Exception as e: 

100 raise TypeError(f"Failed to execute task function: {str(e)}") 

101 

102 raise TypeError(f"Task must be callable or a coroutine, got {type(task)}") 

103 

104 def _update_progress( 

105 self, 

106 progress_callback: Optional[Callable[[int, int], None]], 

107 completed: int, 

108 total: int, 

109 ) -> None: 

110 """Update progress callback if provided.""" 

111 if progress_callback is not None: 

112 try: 

113 progress_callback(completed, total) 

114 except Exception as e: 

115 # Log progress callback error but don't fail the entire process 

116 print(f"Warning: Progress callback failed: {str(e)}")