import collections
import datetime
import functools
import itertools
from types import TracebackType
from typing import Any, Callable, cast, Iterable, Iterator, List, Optional, Sequence, Type, TypeVar, Union
from typing_extensions import Protocol
_T = TypeVar('_T')
[docs]def listify(obj: Union[_T, Sequence[_T], None], none_empty: bool=False) -> List[_T]:
"""Turns ``obj`` into a list. Returns ``[obj]`` if it.
Returns:
``obj`` is simply returned, if it already is a list.
Otherwise - or if it a string - it is wrapped in a list.
If ``none_empty`` is set to ``True``, an empty list is returned, if
``obj`` is ``None``.
"""
if obj is None and none_empty:
return []
elif isinstance(obj, list):
return obj
elif isinstance(obj, (str, bytes)):
return [cast(_T, obj)]
elif isinstance(obj, Sequence):
return list(obj)
else:
return [cast(_T, obj)]
[docs]def consume(it: Iterable[Any], n: Optional[int]=None) -> None:
"""Advance the iterable it n-steps ahead. If n is None, consume entirely.
Copied from:
https://docs.python.org/3.7/library/itertools.html#itertools-recipes
"""
if n is None:
collections.deque(it, maxlen=0)
else:
next(itertools.islice(it, n, n), None)
min_datetime = datetime.datetime.min.replace(tzinfo=datetime.timezone.utc)
"""Minimum representable datetime with timezone ("aware") set to UTC."""
max_datetime = datetime.datetime.max.replace(tzinfo=datetime.timezone.utc)
"""Maximum representable datetime with timezone ("aware") set to UTC."""
[docs]class HandlersCollector(Protocol):
self,
callback: Callable[..., Any],
*args: Any,
**kwargs: Any,
) -> Callable[..., Any]: ...
[docs]class HandlersList(object):
def __init__(self) -> None:
self._handlers: List[Callable[[], None]] = []
[docs] def callback(
self,
callback: Callable[..., Any],
*args: Any,
**kwargs: Any,
) -> Callable[..., Any]:
self._handlers.append(functools.partial(callback, *args, **kwargs))
return callback
def __iadd__(self, other: Iterable[Callable[[], None]]) -> 'HandlersList':
self._handlers += other
return self
[docs] def clear(self) -> None:
self._handlers.clear()
def __iter__(self) -> Iterator[Callable[[], None]]:
return iter(self._handlers)
def __call__(self) -> None:
for handler in self._handlers:
handler()
[docs]class CallbackOnException(object):
"""Context manager calling a function on exit only if an exception occurred.
"""
def __init__(self, f: Callable[..., None], *args: Any, **kwargs: Any) -> None:
self._callback: Callable[[], None] = functools.partial(f, *args, **kwargs)
def __enter__(self) -> 'CallbackOnException':
return self
def __exit__(
self,
exc_type: Optional[Type[BaseException]] = None,
exc_val: Optional[BaseException] = None,
exc_tb: Optional[TracebackType] = None,
) -> Optional[bool]:
if exc_type is not None:
self._callback()
return None