qq_lib.core.repeater

Utility for repeated execution with per-item error handling.

This module provides the Repeater class, which runs a function over a list of items while capturing exceptions, invoking registered handlers, and tracking errors on a per-item basis.

 1# Released under MIT License.
 2# Copyright (c) 2025-2026 Ladislav Bartos and Robert Vacha Lab
 3
 4"""
 5Utility for repeated execution with per-item error handling.
 6
 7This module provides the `Repeater` class, which runs a function over a list of
 8items while capturing exceptions, invoking registered handlers, and tracking
 9errors on a per-item basis.
10"""
11
12from collections.abc import Callable
13from typing import Any, Self
14
15
16class Repeater:
17    """
18    Execute a given function repeatedly for a collection of items,
19    with optional per-exception handling and tracking of encountered errors.
20
21    Attributes:
22        items (list[Any]): List of items to process.
23        encountered_errors (dict[int, BaseException]): A dictionary mapping
24            item indices to exceptions encountered during execution.
25        current_iteration (int): The index of the item currently being processed.
26
27    Args:
28        items (list[Any]): A list of items to iterate over.
29        func (Callable): Function to execute for each item. The item will be passed
30            as the first argument, followed by any `*args` and `**kwargs`.
31        *args (Any): Positional arguments forwarded to `func`.
32        **kwargs (Any): Keyword arguments forwarded to `func`.
33    """
34
35    def __init__(
36        self,
37        items: list[Any],
38        func: Callable,
39        *args: Any,
40        **kwargs: Any,
41    ):
42        self.encountered_errors = {}
43        self.items = items
44        self.current_iteration = 0
45
46        self._handlers: dict[
47            type[BaseException], Callable[[BaseException, Self], Any]
48        ] = {}
49        self._func = func
50        self._args = args
51        self._kwargs = kwargs
52
53    def on_exception(
54        self,
55        exc_type: type[BaseException],
56        handler: Callable[[BaseException, Self], Any],
57    ) -> None:
58        """
59        Register a handler function for a specific exception type.
60
61        Args:
62            exc_type (type[BaseException]): The exception type to handle.
63            handler (Callable): Function to call when `exc_type` is raised.
64                The handler must accept two arguments:
65                - BaseException: The caught exception instance.
66                - Repeater: Reference to this `Repeater` instance.
67        """
68        self._handlers[exc_type] = handler
69
70    def run(self) -> None:
71        """
72        Execute the target function for all items, invoking handlers for exceptions.
73
74        Iterates over all items, calling the provided function with each one.
75        If an exception is raised and a handler for that exception type
76        is registered, the handler is called.
77
78        Unhandled exceptions propagate normally and interrupt the iteration.
79
80        Exceptions are recorded in `encountered_errors`, mapping the item's
81        index to the raised exception instance.
82        """
83        for i, item in enumerate(self.items):
84            self.current_iteration = i
85            try:
86                self._func(item, *self._args, **self._kwargs)
87            except tuple(self._handlers.keys()) as e:
88                self.encountered_errors[i] = e
89                handler = self._handlers[type(e)]
90                handler(e, self)
class Repeater:
17class Repeater:
18    """
19    Execute a given function repeatedly for a collection of items,
20    with optional per-exception handling and tracking of encountered errors.
21
22    Attributes:
23        items (list[Any]): List of items to process.
24        encountered_errors (dict[int, BaseException]): A dictionary mapping
25            item indices to exceptions encountered during execution.
26        current_iteration (int): The index of the item currently being processed.
27
28    Args:
29        items (list[Any]): A list of items to iterate over.
30        func (Callable): Function to execute for each item. The item will be passed
31            as the first argument, followed by any `*args` and `**kwargs`.
32        *args (Any): Positional arguments forwarded to `func`.
33        **kwargs (Any): Keyword arguments forwarded to `func`.
34    """
35
36    def __init__(
37        self,
38        items: list[Any],
39        func: Callable,
40        *args: Any,
41        **kwargs: Any,
42    ):
43        self.encountered_errors = {}
44        self.items = items
45        self.current_iteration = 0
46
47        self._handlers: dict[
48            type[BaseException], Callable[[BaseException, Self], Any]
49        ] = {}
50        self._func = func
51        self._args = args
52        self._kwargs = kwargs
53
54    def on_exception(
55        self,
56        exc_type: type[BaseException],
57        handler: Callable[[BaseException, Self], Any],
58    ) -> None:
59        """
60        Register a handler function for a specific exception type.
61
62        Args:
63            exc_type (type[BaseException]): The exception type to handle.
64            handler (Callable): Function to call when `exc_type` is raised.
65                The handler must accept two arguments:
66                - BaseException: The caught exception instance.
67                - Repeater: Reference to this `Repeater` instance.
68        """
69        self._handlers[exc_type] = handler
70
71    def run(self) -> None:
72        """
73        Execute the target function for all items, invoking handlers for exceptions.
74
75        Iterates over all items, calling the provided function with each one.
76        If an exception is raised and a handler for that exception type
77        is registered, the handler is called.
78
79        Unhandled exceptions propagate normally and interrupt the iteration.
80
81        Exceptions are recorded in `encountered_errors`, mapping the item's
82        index to the raised exception instance.
83        """
84        for i, item in enumerate(self.items):
85            self.current_iteration = i
86            try:
87                self._func(item, *self._args, **self._kwargs)
88            except tuple(self._handlers.keys()) as e:
89                self.encountered_errors[i] = e
90                handler = self._handlers[type(e)]
91                handler(e, self)

Execute a given function repeatedly for a collection of items, with optional per-exception handling and tracking of encountered errors.

Attributes:
  • items (list[Any]): List of items to process.
  • encountered_errors (dict[int, BaseException]): A dictionary mapping item indices to exceptions encountered during execution.
  • current_iteration (int): The index of the item currently being processed.
Arguments:
  • items (list[Any]): A list of items to iterate over.
  • func (Callable): Function to execute for each item. The item will be passed as the first argument, followed by any *args and **kwargs.
  • *args (Any): Positional arguments forwarded to func.
  • **kwargs (Any): Keyword arguments forwarded to func.
Repeater(items: list[typing.Any], func: Callable, *args: Any, **kwargs: Any)
36    def __init__(
37        self,
38        items: list[Any],
39        func: Callable,
40        *args: Any,
41        **kwargs: Any,
42    ):
43        self.encountered_errors = {}
44        self.items = items
45        self.current_iteration = 0
46
47        self._handlers: dict[
48            type[BaseException], Callable[[BaseException, Self], Any]
49        ] = {}
50        self._func = func
51        self._args = args
52        self._kwargs = kwargs
encountered_errors
items
current_iteration
def on_exception( self, exc_type: type[BaseException], handler: Callable[[BaseException, typing.Self], typing.Any]) -> None:
54    def on_exception(
55        self,
56        exc_type: type[BaseException],
57        handler: Callable[[BaseException, Self], Any],
58    ) -> None:
59        """
60        Register a handler function for a specific exception type.
61
62        Args:
63            exc_type (type[BaseException]): The exception type to handle.
64            handler (Callable): Function to call when `exc_type` is raised.
65                The handler must accept two arguments:
66                - BaseException: The caught exception instance.
67                - Repeater: Reference to this `Repeater` instance.
68        """
69        self._handlers[exc_type] = handler

Register a handler function for a specific exception type.

Arguments:
  • exc_type (type[BaseException]): The exception type to handle.
  • handler (Callable): Function to call when exc_type is raised. The handler must accept two arguments:
    • BaseException: The caught exception instance.
    • Repeater: Reference to this Repeater instance.
def run(self) -> None:
71    def run(self) -> None:
72        """
73        Execute the target function for all items, invoking handlers for exceptions.
74
75        Iterates over all items, calling the provided function with each one.
76        If an exception is raised and a handler for that exception type
77        is registered, the handler is called.
78
79        Unhandled exceptions propagate normally and interrupt the iteration.
80
81        Exceptions are recorded in `encountered_errors`, mapping the item's
82        index to the raised exception instance.
83        """
84        for i, item in enumerate(self.items):
85            self.current_iteration = i
86            try:
87                self._func(item, *self._args, **self._kwargs)
88            except tuple(self._handlers.keys()) as e:
89                self.encountered_errors[i] = e
90                handler = self._handlers[type(e)]
91                handler(e, self)

Execute the target function for all items, invoking handlers for exceptions.

Iterates over all items, calling the provided function with each one. If an exception is raised and a handler for that exception type is registered, the handler is called.

Unhandled exceptions propagate normally and interrupt the iteration.

Exceptions are recorded in encountered_errors, mapping the item's index to the raised exception instance.