3 from abc import ABC, abstractmethod
6 from typing import Dict, Optional, Set
8 from pyutils.decorator_utils import invocation_logged
11 logger = logging.getLogger(__name__)
15 """Base class for something that can render."""
26 class abstaining_renderer(renderer):
27 """A renderer that doesn't do it all the time."""
29 def __init__(self, name_to_timeout_dict: Dict[str, int]) -> None:
30 self.name_to_timeout_dict = name_to_timeout_dict
32 for key in name_to_timeout_dict:
33 self.last_runs[key] = 0.0
35 def should_render(self, keys_to_skip: Set[str]) -> Optional[str]:
37 for key in self.name_to_timeout_dict:
39 (now - self.last_runs[key]) > self.name_to_timeout_dict[key]
40 ) and key not in keys_to_skip:
45 def render(self) -> None:
46 tries_per_key: Dict[str, int] = {}
47 keys_to_skip: Set[str] = set()
49 key = self.should_render(keys_to_skip)
51 logger.info(f'Found nothing to do in "{self.get_name()}"; returning.')
54 if key in tries_per_key:
55 tries_per_key[key] += 1
57 tries_per_key[key] = 0
58 op = f"{self.get_name()}.{key}"
60 if tries_per_key[key] >= 3:
61 logger.warning(f'Too many failures in "{op}"; giving up.')
64 msg = f'Executing "{op}"'
65 if tries_per_key[key] > 1:
66 msg = msg + f" (retry #{tries_per_key[key]})"
68 if self.periodic_render(key):
69 logger.debug(f'"{op}" succeeded.')
70 self.last_runs[key] = time.time()
72 logger.warning(f'"{op}" failed; returned False.')
76 def periodic_render(self, key) -> bool:
79 def get_name(self) -> str:
80 return self.__class__.__name__