Source code for clanimtk.util
# -*- coding: utf-8 -*-
# pylint: disable=too-few-public-methods
"""
.. module:: util
:synopsis: This module contains utility functions.
.. moduleauthor:: Simon Larsén <slarse@kth.se>
"""
import asyncio
import functools
import itertools
from contextlib import contextmanager
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Event
from clanimtk import types
from clanimtk.cli import animate_cli, BACKSPACE
BACKSPACE_GEN = lambda size: itertools.cycle([BACKSPACE * size])
BACKLINE_GEN = lambda lines: itertools.cycle(['\033[F' * (lines - 1)])
[docs]def get_supervisor(func: types.AnyFunction) -> types.Supervisor:
"""Get the appropriate supervisor to use and pre-apply the function.
Args:
func: A function.
"""
if not callable(func):
raise TypeError("func is not callable")
if asyncio.iscoroutinefunction(func):
supervisor = _async_supervisor
else:
supervisor = _sync_supervisor
return functools.partial(supervisor, func)
@contextmanager
def _terminating_event():
"""A contextmanager that yields an Event object, which is always set
(Event.set()) when the context exits, regardless of how it exits.
"""
event = Event()
try:
yield event
except:
raise
finally:
event.set()
async def _async_supervisor(func, animation_, step, *args, **kwargs):
"""Supervisor for running an animation with an asynchronous function.
Args:
func: A function to be run alongside an animation.
animation_: An infinite generator that produces
strings for the animation.
step: Seconds between each animation frame.
*args: Arguments for func.
**kwargs: Keyword arguments for func.
Returns:
The result of func(*args, **kwargs)
Raises:
Any exception that is thrown when executing func.
"""
with ThreadPoolExecutor(max_workers=2) as pool:
with _terminating_event() as event:
pool.submit(animate_cli, animation_, step, event)
result = await func(*args, **kwargs)
return result
def _sync_supervisor(func, animation_, step, *args, **kwargs):
"""Supervisor for running an animation with a synchronous function.
Args:
func: A function to be run alongside an animation.
animation_: An infinite generator that produces
strings for the animation.
step: Seconds between each animation frame.
args: Arguments for func.
kwargs: Keyword arguments for func.
Returns:
The result of func(*args, **kwargs)
"""
with ThreadPoolExecutor(max_workers=1) as pool:
with _terminating_event() as event:
pool.submit(animate_cli, animation_, step, event)
result = func(*args, **kwargs)
return result
[docs]def concatechain(*generators: types.FrameGenerator, separator: str = ''):
"""Return a generator that in each iteration takes one value from each of the
supplied generators, joins them together with the specified separator and
yields the result. Stops as soon as any iterator raises StopIteration and
returns the value contained in it.
Primarily created for chaining string generators, hence the name.
Args:
generators: Any number of generators that yield types that can be
joined together with the separator string.
separator: A separator to insert between each value yielded by
the different generators.
Returns:
A generator that yields strings that are the concatenation of one value
from each of the generators, joined together with the separator string.
"""
while True:
try:
next_ = [next(gen) for gen in generators]
yield separator.join(next_)
except StopIteration as exc:
return exc.value