"""Utility functions with no external dependencies."""
import signal
[docs]def is_listlike(x):
return isinstance(x, (list, tuple))
[docs]def to_iterable(x):
return [x] if not is_listlike(x) else x
[docs]def from_iterable(x):
return x[0] if (is_listlike(x) and len(x) == 1) else x
[docs]def robust_len(x):
return len(x) if is_listlike(x) else 1
[docs]def as_tuple_of_len(x, len_):
if is_listlike(x):
assert len(x) == len_, \
"Listlike object of len {} can't be returned " \
"as a tuple of length {}.".format(len(x), len_)
return tuple(x)
else:
return (x,) * len_
[docs]def has_callable_attr(object_, name):
return hasattr(object_, name) and callable(getattr(object_, name))
[docs]def is_maybe_list_of(check_function):
def decorated_function(object_, **kwargs):
if isinstance(object_, (list, tuple)):
return all([check_function(_object, **kwargs) for _object in object_])
else:
return check_function(object_, **kwargs)
return decorated_function
[docs]class delayed_keyboard_interrupt(object):
"""
Delays SIGINT over critical code.
Borrowed from:
https://stackoverflow.com/questions/842557/
how-to-prevent-a-block-of-code-from-being-interrupted-by-keyboardinterrupt-in-py
"""
# PEP8: Context manager class in lowercase
def __enter__(self):
self.signal_received = False
self.old_handler = signal.getsignal(signal.SIGINT)
signal.signal(signal.SIGINT, self.handler)
[docs] def handler(self, sig, frame):
self.signal_received = (sig, frame)
def __exit__(self, type, value, traceback):
signal.signal(signal.SIGINT, self.old_handler)
if self.signal_received:
self.old_handler(*self.signal_received)
[docs]def get_config_for_name(config, name):
config_for_name = {}
for key, val in config.items():
if isinstance(val, dict) and name in val:
# we leave the slicing_config validation to classes higher up in MRO
config_for_name.update({key: val.get(name)})
else:
config_for_name.update({key: val})
return config_for_name