Generic batch processor with progress tracking and error handling
| 67 | ) |
| 68 | |
| 69 | class BatchProcessor: |
| 70 | """Generic batch processor with progress tracking and error handling""" |
| 71 | |
| 72 | def __init__(self, batch_size: int = 50, enable_gc: bool = True): |
| 73 | self.batch_size = batch_size |
| 74 | self.enable_gc = enable_gc |
| 75 | |
| 76 | def process_in_batches( |
| 77 | self, |
| 78 | items: List[Any], |
| 79 | process_func: Callable, |
| 80 | operation_name: str = "Processing", |
| 81 | **kwargs |
| 82 | ) -> List[Any]: |
| 83 | """ |
| 84 | Process items in batches with progress tracking |
| 85 | |
| 86 | Args: |
| 87 | items: List of items to process |
| 88 | process_func: Function to process each batch |
| 89 | operation_name: Name for progress reporting |
| 90 | **kwargs: Additional arguments passed to process_func |
| 91 | |
| 92 | Returns: |
| 93 | List of results from all batches |
| 94 | """ |
| 95 | if not items: |
| 96 | logger.info(f"{operation_name}: No items to process") |
| 97 | return [] |
| 98 | |
| 99 | tracker = ProgressTracker(len(items), operation_name) |
| 100 | results = [] |
| 101 | |
| 102 | logger.info(f"Starting {operation_name} for {len(items)} items in batches of {self.batch_size}") |
| 103 | |
| 104 | with timer(f"{operation_name} (total)"): |
| 105 | for i in range(0, len(items), self.batch_size): |
| 106 | batch = items[i:i + self.batch_size] |
| 107 | batch_num = i // self.batch_size + 1 |
| 108 | total_batches = (len(items) + self.batch_size - 1) // self.batch_size |
| 109 | |
| 110 | try: |
| 111 | with timer(f"Batch {batch_num}/{total_batches}"): |
| 112 | batch_results = process_func(batch, **kwargs) |
| 113 | results.extend(batch_results) |
| 114 | |
| 115 | tracker.update(len(batch)) |
| 116 | |
| 117 | except Exception as e: |
| 118 | logger.error(f"Error in batch {batch_num}: {e}") |
| 119 | tracker.update(len(batch), errors=len(batch)) |
| 120 | # Continue processing other batches |
| 121 | continue |
| 122 | |
| 123 | # Optional garbage collection to manage memory |
| 124 | if self.enable_gc and batch_num % 5 == 0: |
| 125 | gc.collect() |
| 126 |
no outgoing calls
no test coverage detected