python_examples/menu/menu_utils.py

80 lines
2.1 KiB
Python

import asyncio
import time
from itertools import islice
def chunks(iterator, size):
"""Yield successive n-sized chunks from an iterator."""
iterator = iter(iterator)
while True:
chunk = list(islice(iterator, size))
if not chunk:
break
yield chunk
def run_async(coro):
"""Run an async function in a synchronous context."""
try:
_ = asyncio.get_running_loop()
return asyncio.create_task(coro)
except RuntimeError:
return asyncio.run(coro)
class TimingMixin:
def _start_timer(self):
self.start_time = time.perf_counter()
def _stop_timer(self):
self.end_time = time.perf_counter()
self._duration = self.end_time - self.start_time
def get_duration(self) -> float | None:
return getattr(self, "_duration", None)
class MenuError(Exception):
"""Custom exception for the Menu class."""
class OptionAlreadyExistsError(MenuError):
"""Exception raised when an option with the same key already exists in the menu."""
class InvalidHookError(MenuError):
"""Exception raised when a hook is not callable."""
class InvalidActionError(MenuError):
"""Exception raised when an action is not callable."""
class NotAMenuError(MenuError):
"""Exception raised when the provided submenu is not an instance of Menu."""
class CaseInsensitiveDict(dict):
"""A case-insensitive dictionary that treats all keys as uppercase."""
def __setitem__(self, key, value):
super().__setitem__(key.upper(), value)
def __getitem__(self, key):
return super().__getitem__(key.upper())
def __contains__(self, key):
return super().__contains__(key.upper())
def get(self, key, default=None):
return super().get(key.upper(), default)
def pop(self, key, default=None):
return super().pop(key.upper(), default)
def update(self, other=None, **kwargs):
if other:
other = {k.upper(): v for k, v in other.items()}
kwargs = {k.upper(): v for k, v in kwargs.items()}
super().update(other, **kwargs)